From fd12a0237e915d2c88601bb475532e5e8b0f53f9 Mon Sep 17 00:00:00 2001 From: oberrich Date: Wed, 5 Feb 2025 09:30:29 +0100 Subject: [PATCH 01/66] chore: bump `itertools` semver and activate `use_alloc` --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index b0a5bbb082..7ba27c1658 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,7 +31,7 @@ clang-sys = "1" clap = "4" clap_complete = "4" env_logger = "0.10.0" -itertools = { version = ">=0.10,<0.14", default-features = false } +itertools = { version = ">=0.10,<0.15", default-features = false, features = ["use_alloc"] } libloading = "0.8" log = "0.4" objc = "0.2" From 75860ae24e784598a6d3e887ccc40157ee36a9d7 Mon Sep 17 00:00:00 2001 From: oberrich Date: Wed, 5 Feb 2025 09:37:52 +0100 Subject: [PATCH 02/66] feat(integration): add integration tests for custom attributes --- Cargo.lock | 1 + bindgen-integration/Cargo.toml | 3 ++ bindgen-integration/build.rs | 10 +++++- bindgen-integration/src/lib.rs | 57 ++++++++++++++++++++++++++++++++++ 4 files changed, 70 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 4e88c224f5..7b7ea3eab3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -71,6 +71,7 @@ version = "0.1.0" dependencies = [ "bindgen", "cc", + "syn 2.0.90", ] [[package]] diff --git a/bindgen-integration/Cargo.toml b/bindgen-integration/Cargo.toml index 5c8c89d528..4d5c064aae 100644 --- a/bindgen-integration/Cargo.toml +++ b/bindgen-integration/Cargo.toml @@ -10,6 +10,9 @@ build = "build.rs" rust-version.workspace = true edition.workspace = true +[dependencies] +syn = { workspace = true, features = ["full", "visit"] } + [build-dependencies] bindgen = { workspace = true, default-features = true, features = ["experimental"] } cc.workspace = true diff --git a/bindgen-integration/build.rs b/bindgen-integration/build.rs index 583f46ea93..e6598570bb 100644 --- a/bindgen-integration/build.rs +++ b/bindgen-integration/build.rs @@ -1,7 +1,8 @@ extern crate bindgen; use bindgen::callbacks::{ - DeriveInfo, IntKind, MacroParsingBehavior, ParseCallbacks, + AttributeItemKind, DeriveInfo, FunctionKind, IntKind, MacroParsingBehavior, + ParseCallbacks, }; use bindgen::{Builder, EnumVariation, Formatter}; use std::collections::HashSet; @@ -140,7 +141,14 @@ impl ParseCallbacks for MacroCallback { info: &bindgen::callbacks::AttributeInfo<'_>, ) -> Vec { if info.name == "Test" { + assert_eq!(info.kind, AttributeItemKind::Struct); vec!["#[cfg_attr(test, derive(PartialOrd))]".into()] + } else if info.name == "coord" { + assert_eq!( + info.kind, + AttributeItemKind::Function(FunctionKind::Function) + ); + vec!["#[must_use]".into()] } else { vec![] } diff --git a/bindgen-integration/src/lib.rs b/bindgen-integration/src/lib.rs index 13f5bd889a..9786c16e8e 100755 --- a/bindgen-integration/src/lib.rs +++ b/bindgen-integration/src/lib.rs @@ -297,6 +297,63 @@ fn test_custom_derive() { assert!(!(test1 > test2)); } +#[test] +fn test_custom_fn_attribute() { + use std::env; + use std::fs; + use std::path::Path; + use syn::visit::Visit; + use syn::{parse_file, File, ForeignItem, Item, ItemForeignMod}; + + let out_dir = + std::env::var("OUT_DIR").expect("OUT_DIR environment variable not set"); + let test_file_path = Path::new(&out_dir).join("test.rs"); + let file_content = fs::read_to_string(&test_file_path) + .expect("Failed to read test.rs file"); + let syntax_tree: File = + parse_file(&file_content).expect("Failed to parse test.rs"); + + struct FunctionVisitor { + found_coord: bool, + has_must_use: bool, + } + + impl<'ast> Visit<'ast> for FunctionVisitor { + fn visit_item_foreign_mod( + &mut self, + foreign_mod: &'ast ItemForeignMod, + ) { + for foreign_item in &foreign_mod.items { + if let ForeignItem::Fn(item_fn) = foreign_item { + if item_fn.sig.ident == "coord" { + self.found_coord = true; + self.has_must_use = item_fn + .attrs + .iter() + .any(|attr| attr.path().is_ident("must_use")); + } + } + } + } + } + + let mut visitor = FunctionVisitor { + found_coord: false, + has_must_use: false, + }; + visitor.visit_file(&syntax_tree); + + assert!( + visitor.found_coord, + "The function 'coord' was not found in the source." + ); + assert!( + visitor.has_must_use, + "The function 'coord' does not have the #[must_use] attribute." + ); +} + + #[test] fn test_custom_attributes() { // The `add_attributes` callback should have added `#[cfg_attr(test, derive(PartialOrd))])` From 2c4742fce3b284afa1f8781ef8c748a01e09320a Mon Sep 17 00:00:00 2001 From: oberrich Date: Wed, 5 Feb 2025 09:48:31 +0100 Subject: [PATCH 03/66] feat(attrs): add tests for custom attributes and cli --- bindgen-tests/build.rs | 2 +- .../tests/expectations/tests/attribute-custom-cli.rs | 6 ++++++ bindgen-tests/tests/expectations/tests/attribute-custom.rs | 6 ++++++ bindgen-tests/tests/headers/attribute-custom-cli.h | 3 ++- bindgen-tests/tests/headers/attribute-custom.h | 5 +++++ 5 files changed, 20 insertions(+), 2 deletions(-) diff --git a/bindgen-tests/build.rs b/bindgen-tests/build.rs index b261ed0a35..8fb33716a1 100644 --- a/bindgen-tests/build.rs +++ b/bindgen-tests/build.rs @@ -23,7 +23,7 @@ pub fn main() { for entry in entries { // TODO: file_is_cpp() in bindgen/lib.rs checks for hpp,hxx,hh, and h++ - should this be consistent? - if entry.path().extension().map_or(false, |ext| { + if entry.path().extension().is_some_and(|ext| { ext.eq_ignore_ascii_case("h") || ext.eq_ignore_ascii_case("hpp") }) { let func = entry diff --git a/bindgen-tests/tests/expectations/tests/attribute-custom-cli.rs b/bindgen-tests/tests/expectations/tests/attribute-custom-cli.rs index 55353116d3..82179b2840 100644 --- a/bindgen-tests/tests/expectations/tests/attribute-custom-cli.rs +++ b/bindgen-tests/tests/expectations/tests/attribute-custom-cli.rs @@ -46,3 +46,9 @@ const _: () = { "Offset of field: non_matching::inner", ][::std::mem::offset_of!(non_matching, inner) - 0usize]; }; +unsafe extern "C" { + #[doc(hidden)] + #[must_use] + #[doc(hidden)] + pub fn foo_function() -> ::std::os::raw::c_int; +} diff --git a/bindgen-tests/tests/expectations/tests/attribute-custom.rs b/bindgen-tests/tests/expectations/tests/attribute-custom.rs index 6d616d3f3e..2b0df9e526 100644 --- a/bindgen-tests/tests/expectations/tests/attribute-custom.rs +++ b/bindgen-tests/tests/expectations/tests/attribute-custom.rs @@ -20,3 +20,9 @@ pub struct my_type2 { pub struct my_type3 { pub a: ::std::os::raw::c_ulong, } +unsafe extern "C" { + #[must_use] + #[doc(hidden)] + ///
+ pub fn function_attributes() -> ::std::os::raw::c_int; +} diff --git a/bindgen-tests/tests/headers/attribute-custom-cli.h b/bindgen-tests/tests/headers/attribute-custom-cli.h index a5f73c78e5..cb004d45e7 100644 --- a/bindgen-tests/tests/headers/attribute-custom-cli.h +++ b/bindgen-tests/tests/headers/attribute-custom-cli.h @@ -1,4 +1,4 @@ -// bindgen-flags: --default-enum-style rust --default-non-copy-union-style manually_drop --no-default=".*" --no-hash=".*" --no-partialeq=".*" --no-debug=".*" --no-copy=".*" --with-attribute-custom="foo_[^e].*=#[doc(hidden)]" --with-attribute-custom-struct="foo.*=#[derive(Default)]" --with-attribute-custom-enum="foo.*=#[cfg_attr(test, derive(PartialOrd, Copy))]" --with-attribute-custom-union="foo.*=#[derive(Clone)],#[derive(Copy)]" +// bindgen-flags: --default-enum-style rust --default-non-copy-union-style manually_drop --no-default=".*" --no-hash=".*" --no-partialeq=".*" --no-debug=".*" --no-copy=".*" --with-attribute-custom="foo_[^e].*=#[doc(hidden)]" --with-attribute-custom-struct="foo.*=#[derive(Default)]" --with-attribute-custom-enum="foo.*=#[cfg_attr(test, derive(PartialOrd, Copy))]" --with-attribute-custom-union="foo.*=#[derive(Clone)],#[derive(Copy)]" --with-attribute-custom-function="foo.*=#[must_use],#[doc(hidden)]" struct foo_struct { int inner; }; @@ -12,3 +12,4 @@ union foo_union { struct non_matching { int inner; }; +int foo_function() { return 1; } diff --git a/bindgen-tests/tests/headers/attribute-custom.h b/bindgen-tests/tests/headers/attribute-custom.h index dd382bf8cd..f825811836 100644 --- a/bindgen-tests/tests/headers/attribute-custom.h +++ b/bindgen-tests/tests/headers/attribute-custom.h @@ -26,3 +26,8 @@ struct my_type2 { struct my_type3 { unsigned long a; }; + +/** + *
+ */ +int function_attributes() { return 1; } From 8506bc9d9a0d853e1f9579b6dbb8c69289e3001e Mon Sep 17 00:00:00 2001 From: oberrich Date: Wed, 5 Feb 2025 11:20:51 +0100 Subject: [PATCH 04/66] feat(attrs): implement codegen --- bindgen/callbacks.rs | 29 ++- bindgen/codegen/mod.rs | 403 +++++++++++++++++++++++++++----------- bindgen/ir/annotations.rs | 2 +- 3 files changed, 310 insertions(+), 124 deletions(-) diff --git a/bindgen/callbacks.rs b/bindgen/callbacks.rs index 8a21e98dea..1d166f5391 100644 --- a/bindgen/callbacks.rs +++ b/bindgen/callbacks.rs @@ -129,13 +129,8 @@ pub trait ParseCallbacks: fmt::Debug { vec![] } - /// Provide a list of custom attributes. - /// - /// If no additional attributes are wanted, this function should return an - /// empty `Vec`. - fn add_attributes(&self, _info: &AttributeInfo<'_>) -> Vec { - vec![] - } + /// Process an item's attribute + fn process_attributes(&self, _info: &AttributeInfo<'_>, _attributes: &mut HashSet) {} /// Process a source code comment. fn process_comment(&self, _comment: &str) -> Option { @@ -232,14 +227,34 @@ pub struct DeriveInfo<'a> { } /// Relevant information about a type to which new attributes will be added using +/// Relevant information about an item to which new attributes will be added using /// [`ParseCallbacks::add_attributes`]. #[derive(Debug)] #[non_exhaustive] pub struct AttributeInfo<'a> { /// The name of the type. + /// The name of the item. pub name: &'a str, /// The kind of the type. pub kind: TypeKind, + /// The kind of the item. + pub kind: AttributeItemKind, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +/// The kind of the current item. +pub enum AttributeItemKind { + /// The item is a Rust `struct`. + Struct, + /// The item is a Rust `enum`. + Enum, + /// The item is a Rust `union`. + Union, + /// The item is a Rust variable. + Var, + /// The item is a Rust `fn`. + Function(FunctionKind), + // TODO: Add `Alias` variant and more information about other items } #[derive(Debug, Clone, Copy, PartialEq, Eq)] diff --git a/bindgen/codegen/mod.rs b/bindgen/codegen/mod.rs index f58a234117..0f284fdc52 100644 --- a/bindgen/codegen/mod.rs +++ b/bindgen/codegen/mod.rs @@ -21,8 +21,8 @@ use self::struct_layout::StructLayoutTracker; use super::BindgenOptions; use crate::callbacks::{ - AttributeInfo, DeriveInfo, DiscoveredItem, DiscoveredItemId, FieldInfo, - TypeKind as DeriveTypeKind, + AttributeInfo, AttributeItemKind, DeriveInfo, DiscoveredItem, + DiscoveredItemId, FieldInfo, TypeKind as DeriveTypeKind, }; use crate::codegen::error::Error; use crate::ir::analysis::{HasVtable, Sizedness}; @@ -55,16 +55,19 @@ use crate::ir::template::{ use crate::ir::ty::{Type, TypeKind}; use crate::ir::var::Var; -use proc_macro2::{Ident, Span}; +use proc_macro2::{Ident, Span, TokenStream}; use quote::{ToTokens, TokenStreamExt}; - +use syn::parse::ParseStream; +use syn::token::Pound; +use syn::{parse, parse2, parse_quote, parse_str, Attribute, Block}; + use crate::{Entry, HashMap, HashSet}; use std::borrow::Cow; use std::cell::Cell; use std::collections::VecDeque; use std::ffi::CStr; use std::fmt::{self, Write}; -use std::ops; +use std::ops::{self, Deref}; use std::str::{self, FromStr}; #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -220,6 +223,63 @@ impl From for Vec<&'static str> { } } +fn parse_tokens(it: I) -> Vec +where + I: IntoIterator, + T: AsRef, +{ + it.into_iter() + .map(|s| s.as_ref().parse().expect("tokens are valid")) + .collect() +} + +fn normalize_attributes(attrs: HashSet) -> HashSet { + attrs + .iter() + .map(|attr| { + let mut in_quotes = None; + attr.chars() + .filter_map(|c| match c { + '"' | '\'' if in_quotes.is_none() => { + in_quotes = Some(c); + Some(c) + } + c if in_quotes == Some(c) => { + in_quotes = None; + Some(c) + } + c if in_quotes.is_some() => Some(c), + c if !c.is_whitespace() => Some(c), + _ => None, + }) + .collect() + }) + .collect() +} + +fn process_attributes( + result: &mut CodegenResult, + item: &Item, + ctx: &BindgenContext, + attrs: HashSet, + kind: AttributeItemKind, +) -> Vec { + // TODO: attrs.extend(parse_tokens(item.annotations().attributes())); + + let mut attrs = normalize_attributes(attrs); + ctx.options().for_each_callback_mut(|cb| { + cb.process_attributes( + &AttributeInfo { + name: &item.canonical_name(ctx), + kind, + }, + &mut attrs, + ); + }); + result.set_attributes(item.id(), attrs.clone()); + parse_tokens(normalize_attributes(attrs)) +} + struct WrapAsVariadic { new_name: String, idx_of_va_list_arg: usize, @@ -276,6 +336,9 @@ struct CodegenResult<'a> { /// List of items to serialize. With optionally the argument for the wrap as /// variadic transformation to be applied. items_to_serialize: Vec<(ItemId, Option)>, + + /// Tracks attributes of items during codegen + item_attributes: HashMap>, } impl<'a> CodegenResult<'a> { @@ -294,9 +357,21 @@ impl<'a> CodegenResult<'a> { vars_seen: Default::default(), overload_counters: Default::default(), items_to_serialize: Default::default(), + item_attributes: Default::default(), } } + fn set_attributes(&mut self, item_id: ItemId, attributes: HashSet) { + *self + .item_attributes + .entry(item_id) + .or_insert_with(HashSet::default) = attributes; + } + + fn get_attributes(&self, item_id: ItemId) -> Option<&HashSet> { + self.item_attributes.get(&item_id) + } + fn dynamic_items(&mut self) -> &mut DynamicItems { &mut self.dynamic_items } @@ -613,7 +688,7 @@ impl CodeGenerator for Module { for raw_line in raw_lines { found_any = true; result.push( - proc_macro2::TokenStream::from_str(raw_line).unwrap(), + TokenStream::from_str(raw_line).unwrap(), ); } } @@ -680,10 +755,21 @@ impl CodeGenerator for Var { attrs.push(attributes::doc(&comment)); } + attrs.extend(item.annotations().attributes()); + let var_ty = self.ty(); let ty = var_ty.to_rust_ty_or_opaque(ctx, &()); if let Some(val) = self.val() { + // TODO: Review why this is inside the if block + let attrs = process_attributes( + result, + item, + ctx, + attrs, + AttributeItemKind::Var, + ); + match *val { VarType::Bool(val) => { result.push(quote! { @@ -794,6 +880,14 @@ impl CodeGenerator for Var { } }); + let attrs = process_attributes( + result, + item, + ctx, + attrs, + AttributeItemKind::Var, + ); + let maybe_mut = if self.is_const() { quote! {} } else { @@ -1007,12 +1101,6 @@ impl CodeGenerator for Type { ); }); - let mut tokens = if let Some(comment) = item.comment(ctx) { - attributes::doc(&comment) - } else { - quote! {} - }; - let alias_style = if ctx.options().type_alias.matches(&name) { AliasVariation::TypeAlias } else if ctx.options().new_type_alias.matches(&name) { @@ -1022,6 +1110,46 @@ impl CodeGenerator for Type { } else { ctx.options().default_alias_style }; + + attrs.extend(item.annotations().attributes().iter().cloned()); + + if let Some(comment) = item.comment(ctx) { + attrs.insert(attributes::doc(&comment).to_string()); + } + + let mut attrs = HashSet::default(); + if let Some(inner_attrs) = + result.get_attributes(inner_item.id()) + { + // Only apply attributes through type aliases when they are relevant to compilation + attrs.extend( + parse_tokens(inner_attrs) + .into_iter() + .filter(|t| !t.is_empty()) + .map(|t| parse_quote! {#t}) + .filter_map(|attr: Attribute| { + if attr.path().is_ident("cfg") || + attr.path().is_ident("link") + { + Some(attr.to_token_stream().to_string()) + } else { + None + } + }), + ); + } + + let attrs = process_attributes( + result, + item, + ctx, + attrs, + AttributeItemKind::Struct, + ); + + let mut tokens = quote! { + #( #attrs )* + }; // We prefer using `pub use` over `pub type` because of: // https://github.com/rust-lang/rust/issues/26264 @@ -1048,8 +1176,7 @@ impl CodeGenerator for Type { pub type #rust_name }, AliasVariation::NewType | AliasVariation::NewTypeDeref => { - let mut attributes = - vec![attributes::repr("transparent")]; + let mut attrs = vec![attributes::repr("transparent")]; let packed = false; // Types can't be packed in Rust. let derivable_traits = derives_of_item(item, ctx, packed); @@ -1066,21 +1193,10 @@ impl CodeGenerator for Type { // In most cases this will be a no-op, since custom_derives will be empty. derives .extend(custom_derives.iter().map(|s| s.as_str())); - attributes.push(attributes::derives(&derives)); - - let custom_attributes = - ctx.options().all_callbacks(|cb| { - cb.add_attributes(&AttributeInfo { - name: &name, - kind: DeriveTypeKind::Struct, - }) - }); - attributes.extend( - custom_attributes - .iter() - .map(|s| s.parse().unwrap()), - ); + attrs.push(attributes::derives(&derives)); + // TODO: Add custom attributes from callback here + quote! { #( #attributes )* pub struct #rust_name @@ -1145,6 +1261,7 @@ impl CodeGenerator for Type { if alias_style == AliasVariation::NewTypeDeref { let prefix = ctx.trait_prefix(); tokens.append_all(quote! { + #( #cfg_attrs )* impl ::#prefix::ops::Deref for #rust_name { type Target = #inner_rust_type; #[inline] @@ -1152,6 +1269,7 @@ impl CodeGenerator for Type { &self.0 } } + #( #cfg_attrs )* impl ::#prefix::ops::DerefMut for #rust_name { #[inline] fn deref_mut(&mut self) -> &mut Self::Target { @@ -1228,10 +1346,10 @@ impl CodeGenerator for Vtable<'_> { let TypeKind::Function(ref signature) = signature_item.expect_type().kind() else { panic!("Function signature type mismatch") }; // FIXME: Is there a canonical name without the class prepended? - let function_name = function_item.canonical_name(ctx); + let canonical_name = function_item.canonical_name(ctx); // FIXME: Need to account for overloading with times_seen (separately from regular function path). - let function_name = ctx.rust_ident(function_name); + let function_name = ctx.rust_ident(&canonical_name); let mut args = utils::fnsig_arguments(ctx, signature); let ret = utils::fnsig_return_ty(ctx, signature); @@ -1393,8 +1511,8 @@ trait FieldCodegen<'a> { methods: &mut M, extra: Self::Extra, ) where - F: Extend, - M: Extend; + F: Extend, + M: Extend; } impl FieldCodegen<'_> for Field { @@ -1414,8 +1532,8 @@ impl FieldCodegen<'_> for Field { methods: &mut M, _: (), ) where - F: Extend, - M: Extend, + F: Extend, + M: Extend, { match *self { Field::DataMember(ref data) => { @@ -1492,8 +1610,8 @@ impl FieldCodegen<'_> for FieldData { methods: &mut M, _: (), ) where - F: Extend, - M: Extend, + F: Extend, + M: Extend, { // Bitfields are handled by `FieldCodegen` implementations for // `BitfieldUnit` and `Bitfield`. @@ -1753,8 +1871,8 @@ impl FieldCodegen<'_> for BitfieldUnit { methods: &mut M, _: (), ) where - F: Extend, - M: Extend, + F: Extend, + M: Extend, { use crate::ir::ty::RUST_DERIVE_IN_ARRAY_LIMIT; @@ -1887,7 +2005,7 @@ impl FieldCodegen<'_> for BitfieldUnit { fn bitfield_getter_name( ctx: &BindgenContext, bitfield: &Bitfield, -) -> proc_macro2::TokenStream { +) -> TokenStream { let name = bitfield.getter_name(); let name = ctx.rust_ident_raw(name); quote! { #name } @@ -1896,7 +2014,7 @@ fn bitfield_getter_name( fn bitfield_raw_getter_name( ctx: &BindgenContext, bitfield: &Bitfield, -) -> proc_macro2::TokenStream { +) -> TokenStream { let name = bitfield.getter_name(); let name = ctx.rust_ident_raw(format!("{name}_raw")); quote! { #name } @@ -1905,7 +2023,7 @@ fn bitfield_raw_getter_name( fn bitfield_setter_name( ctx: &BindgenContext, bitfield: &Bitfield, -) -> proc_macro2::TokenStream { +) -> TokenStream { let setter = bitfield.setter_name(); let setter = ctx.rust_ident_raw(setter); quote! { #setter } @@ -1914,7 +2032,7 @@ fn bitfield_setter_name( fn bitfield_raw_setter_name( ctx: &BindgenContext, bitfield: &Bitfield, -) -> proc_macro2::TokenStream { +) -> TokenStream { let setter = bitfield.setter_name(); let setter = ctx.rust_ident_raw(format!("{setter}_raw")); quote! { #setter } @@ -1952,8 +2070,8 @@ impl<'a> FieldCodegen<'a> for Bitfield { &'a mut FieldVisibilityKind, ), ) where - F: Extend, - M: Extend, + F: Extend, + M: Extend, { let prefix = ctx.trait_prefix(); let getter_name = bitfield_getter_name(ctx, self); @@ -2404,14 +2522,14 @@ impl CodeGenerator for CompInfo { (quote! {}, quote! {}, quote! {}) }; - let mut attributes = vec![]; + let mut attrs = vec![]; let mut needs_clone_impl = false; let mut needs_default_impl = false; let mut needs_debug_impl = false; let mut needs_partialeq_impl = false; let needs_flexarray_impl = flex_array_generic.is_some(); if let Some(comment) = item.comment(ctx) { - attributes.push(attributes::doc(&comment)); + attrs.push(attributes::doc(&comment)); } // if a type has both a "packed" attribute and an "align(N)" attribute, then check if the @@ -2428,9 +2546,9 @@ impl CodeGenerator for CompInfo { } else { format!("packed({n})") }; - attributes.push(attributes::repr_list(&["C", &packed_repr])); + attrs.push(attributes::repr_list(&["C", &packed_repr])); } else { - attributes.push(attributes::repr("C")); + attrs.push(attributes::repr("C")); } if true { @@ -2438,7 +2556,7 @@ impl CodeGenerator for CompInfo { // Ensure that the struct has the correct alignment even in // presence of alignas. let explicit = helpers::ast_ty::int_expr(explicit as i64); - attributes.push(quote! { + attrs.push(quote! { #[repr(align(#explicit))] }); } @@ -2521,40 +2639,43 @@ impl CodeGenerator for CompInfo { derives.extend(custom_derives.iter().map(|s| s.as_str())); if !derives.is_empty() { - attributes.push(attributes::derives(&derives)); + attrs.push(attributes::derives(&derives)); } - attributes.extend( - item.annotations() - .attributes() - .iter() - .map(|s| s.parse().unwrap()), - ); - - let custom_attributes = ctx.options().all_callbacks(|cb| { - cb.add_attributes(&AttributeInfo { - name: &canonical_name, - kind: if is_rust_union { - DeriveTypeKind::Union - } else { - DeriveTypeKind::Struct - }, - }) - }); - attributes.extend(custom_attributes.iter().map(|s| s.parse().unwrap())); - if item.must_use(ctx) { - attributes.push(attributes::must_use()); + attrs.insert(attributes::must_use().to_string()); } + attrs.extend(item.annotations().attributes().iter().cloned()); + + let attrs = process_attributes( + result, + item, + ctx, + attrs, + if is_rust_union { + AttributeItemKind::Union + } else { + AttributeItemKind::Struct + } + ); + + let cfg_attrs = attrs + .iter() + .filter(|t| !t.is_empty()) + .map(|t| parse_quote! {#t}) + .filter(|attr: &Attribute| { + attr.path().is_ident("cfg") || attr.path().is_ident("link") + }); + let mut tokens = if is_rust_union { quote! { - #( #attributes )* + #( #attrs )* pub union #canonical_ident } } else { quote! { - #( #attributes )* + #( #attrs )* pub struct #canonical_ident } }; @@ -2753,6 +2874,7 @@ impl CodeGenerator for CompInfo { if needs_clone_impl { result.push(quote! { + #( #cfg_attrs )* impl #impl_generics_labels Clone for #ty_for_impl { fn clone(&self) -> Self { *self } } @@ -2760,6 +2882,7 @@ impl CodeGenerator for CompInfo { } if needs_flexarray_impl { + // TODO: Expand cfg_attrs if necessary result.push(self.generate_flexarray( ctx, &canonical_ident, @@ -2793,6 +2916,7 @@ impl CodeGenerator for CompInfo { // non-zero padding bytes, especially when forwards/backwards compatibility is // involved. result.push(quote! { + #( #cfg_attrs )* impl #impl_generics_labels Default for #ty_for_impl { fn default() -> Self { #body @@ -2812,6 +2936,7 @@ impl CodeGenerator for CompInfo { let prefix = ctx.trait_prefix(); result.push(quote! { + #( #cfg_attrs )* impl #impl_generics_labels ::#prefix::fmt::Debug for #ty_for_impl { #impl_ } @@ -2836,6 +2961,7 @@ impl CodeGenerator for CompInfo { let prefix = ctx.trait_prefix(); result.push(quote! { + #( #cfg_attrs )* impl #impl_generics_labels ::#prefix::cmp::PartialEq for #ty_for_impl #partialeq_bounds { #impl_ } @@ -2845,6 +2971,7 @@ impl CodeGenerator for CompInfo { if !methods.is_empty() { result.push(quote! { + #( #cfg_attrs )* impl #impl_generics_labels #ty_for_impl { #( #methods )* } @@ -2993,7 +3120,7 @@ impl Method { fn codegen_method( &self, ctx: &BindgenContext, - methods: &mut Vec, + methods: &mut Vec, method_names: &mut HashSet, result: &mut CodegenResult<'_>, _parent: &CompInfo, @@ -3063,11 +3190,11 @@ impl Method { method_names.insert(name.clone()); - let mut function_name = function_item.canonical_name(ctx); + let mut canonical_name = function_item.canonical_name(ctx); if times_seen > 0 { - write!(&mut function_name, "{times_seen}").unwrap(); + write!(&mut canonical_name, "{times_seen}").unwrap(); } - let function_name = ctx.rust_ident(function_name); + let function_name = ctx.rust_ident(&canonical_name); let mut args = utils::fnsig_arguments(ctx, signature); let mut ret = utils::fnsig_return_ty(ctx, signature); @@ -3151,6 +3278,14 @@ impl Method { attrs.push(attributes::must_use()); } + let attrs = process_attributes( + result, + function_item, + ctx, + attrs, + AttributeItemKind::Function(FunctionKind::Method(self.kind())), + ); + let name = ctx.rust_ident(&name); methods.push(quote! { #(#attrs)* @@ -3267,23 +3402,26 @@ impl FromStr for EnumVariation { /// A helper type to construct different enum variations. enum EnumBuilder<'a> { Rust { - attrs: Vec, + attrs: Vec, ident: Ident, - tokens: proc_macro2::TokenStream, + tokens: TokenStream, emitted_any_variants: bool, }, NewType { canonical_name: &'a str, - tokens: proc_macro2::TokenStream, + tokens: TokenStream, is_bitfield: bool, is_global: bool, }, Consts { - variants: Vec, + variants: Vec, + // TODO: Investigate if we end up using this + attrs: Vec, }, ModuleConsts { module_name: &'a str, - module_items: Vec, + module_items: Vec, + attrs: Vec, }, } @@ -3297,7 +3435,7 @@ impl<'a> EnumBuilder<'a> { /// the representation, and which variation it should be generated as. fn new( name: &'a str, - mut attrs: Vec, + mut attrs: Vec, repr: &syn::Type, enum_variation: EnumVariation, has_typedef: bool, @@ -3340,7 +3478,7 @@ impl<'a> EnumBuilder<'a> { }); } - EnumBuilder::Consts { variants } + EnumBuilder::Consts { variants, attrs } } EnumVariation::ModuleConsts => { @@ -3356,6 +3494,7 @@ impl<'a> EnumBuilder<'a> { EnumBuilder::ModuleConsts { module_name: name, module_items: vec![type_definition], + attrs } } } @@ -3419,6 +3558,7 @@ impl<'a> EnumBuilder<'a> { let enum_ident = ctx.rust_ident(canonical_name); let variant_ident = ctx.rust_ident(variant_name); + // TODO: Make sure attributes are emitted properly result.push(quote! { impl #enum_ident { #doc @@ -3441,7 +3581,7 @@ impl<'a> EnumBuilder<'a> { self } - EnumBuilder::Consts { .. } => { + EnumBuilder::Consts { ref attrs, .. } => { let constant_name = match mangling_prefix { Some(prefix) => { Cow::Owned(format!("{prefix}_{variant_name}")) @@ -3460,6 +3600,7 @@ impl<'a> EnumBuilder<'a> { EnumBuilder::ModuleConsts { module_name, mut module_items, + attrs } => { let name = ctx.rust_ident(variant_name); let ty = ctx.rust_ident(CONSTIFIED_ENUM_MODULE_REPR_NAME); @@ -3471,6 +3612,7 @@ impl<'a> EnumBuilder<'a> { EnumBuilder::ModuleConsts { module_name, module_items, + attrs } } } @@ -3481,7 +3623,7 @@ impl<'a> EnumBuilder<'a> { ctx: &BindgenContext, rust_ty: &syn::Type, result: &mut CodegenResult<'_>, - ) -> proc_macro2::TokenStream { + ) -> TokenStream { match self { EnumBuilder::Rust { attrs, @@ -3562,10 +3704,12 @@ impl<'a> EnumBuilder<'a> { EnumBuilder::ModuleConsts { module_items, module_name, + attrs, .. } => { let ident = ctx.rust_ident(module_name); quote! { + #( #attrs )* pub mod #ident { #( #module_items )* } @@ -3715,22 +3859,20 @@ impl CodeGenerator for Enum { // In most cases this will be a no-op, since custom_derives will be empty. derives.extend(custom_derives.iter().map(|s| s.as_str())); + // TODO: Try and make sure this makes sense attrs.extend(item.annotations().attributes()); attrs.extend( item.annotations() .attributes() .iter() .map(|s| s.parse().unwrap()), ); - - // The custom attribute callback may return a list of attributes; - // add them to the end of the list. - let custom_attributes = ctx.options().all_callbacks(|cb| { - cb.add_attributes(&AttributeInfo { - name: &name, - kind: DeriveTypeKind::Enum, - }) - }); - attrs.extend(custom_attributes.iter().map(|s| s.parse().unwrap())); + let attrs = process_attributes( + result, + item, + ctx, + attrs, + AttributeItemKind::Enum, + ); attrs.push(attributes::derives(&derives)); } @@ -3771,6 +3913,26 @@ impl CodeGenerator for Enum { let mut builder = EnumBuilder::new(&name, attrs, &repr, variation, has_typedef); + // The custom attribute callback may return a list of attributes; + // add them to the end of the list. + attrs.extend(item.annotations().attributes().iter().cloned()); + + let attrs = process_attributes( + result, + item, + ctx, + attrs, + AttributeItemKind::Enum, + ); + + let mut builder = EnumBuilder::new( + &name, + attrs.clone(), + &repr, + variation, + has_typedef, + ); + // A map where we keep a value -> variant relation. let mut seen_values = HashMap::<_, Ident>::default(); let enum_rust_ty = item.to_rust_ty_or_opaque(ctx, &()); @@ -3832,7 +3994,9 @@ impl CodeGenerator for Enum { let enum_canonical_name = &ident; let variant_name = ctx.rust_ident_raw(&*mangled_name); + // TODO: Only quote in #(#cfg_attrs)* result.push(quote! { + #(#attrs)* impl #enum_rust_ty { pub const #variant_name : #enum_rust_ty = #enum_canonical_name :: #existing_variant_name ; @@ -4305,7 +4469,7 @@ impl TryToRustTy for Type { } TypeKind::Enum(..) => { let path = item.namespace_aware_canonical_path(ctx); - let path = proc_macro2::TokenStream::from_str(&path.join("::")) + let path = TokenStream::from_str(&path.join("::")) .unwrap(); Ok(syn::parse_quote!(#path)) } @@ -4590,25 +4754,24 @@ impl CodeGenerator for Function { result.saw_function(seen_symbol_name); } - let mut attributes = vec![]; - - if true { - let must_use = signature.must_use() || { - let ret_ty = signature - .return_type() - .into_resolver() - .through_type_refs() - .resolve(ctx); - ret_ty.must_use(ctx) - }; + let mut attrs = vec![]; + attrs.extend(parse_tokens(item.annotations().attributes())); + + let must_use = signature.must_use() || { + let ret_ty = signature + .return_type() + .into_resolver() + .through_type_refs() + .resolve(ctx); + ret_ty.must_use(ctx) + }; - if must_use { - attributes.push(attributes::must_use()); - } + if must_use { + attrs.push(attributes::must_use()); } if let Some(comment) = item.comment(ctx) { - attributes.push(attributes::doc(&comment)); + attrs.push(attributes::doc(&comment)); } let abi = match signature.abi(ctx, Some(name)) { @@ -4652,7 +4815,7 @@ impl CodeGenerator for Function { if let Some(link_name) = link_name_attr { if !is_dynamic_function { - attributes.push(attributes::link_name::(link_name)); + attrs.push(attributes::link_name::(link_name)); } } @@ -4670,7 +4833,7 @@ impl CodeGenerator for Function { if should_wrap { let name = canonical_name.clone() + ctx.wrap_static_fns_suffix(); - attributes.push(attributes::link_name::(&name)); + attrs.push(attributes::link_name::(&name)); } let wrap_as_variadic = if should_wrap && !signature.is_variadic() { @@ -4714,11 +4877,19 @@ impl CodeGenerator for Function { .rust_features .unsafe_extern_blocks .then(|| quote!(unsafe)); + + let attrs = process_attributes( + result, + item, + ctx, + attrs, + AttributeItemKind::Function(FunctionKind::Function), + ); let tokens = quote! { #wasm_link_attribute #safety extern #abi { - #(#attributes)* + #( #attrs )* pub fn #ident ( #( #args ),* ) #ret; } }; @@ -4852,7 +5023,7 @@ fn variadic_fn_diagnostic( fn objc_method_codegen( ctx: &BindgenContext, method: &ObjCMethod, - methods: &mut Vec, + methods: &mut Vec, class_name: Option<&str>, rust_class_name: &str, prefix: &str, @@ -5116,7 +5287,7 @@ impl CodeGenerator for ObjCInterface { pub(crate) fn codegen( context: BindgenContext, -) -> Result<(proc_macro2::TokenStream, BindgenOptions), CodegenError> { +) -> Result<(TokenStream, BindgenOptions), CodegenError> { context.gen(|context| { let _t = context.timer("codegen"); let counter = Cell::new(0); diff --git a/bindgen/ir/annotations.rs b/bindgen/ir/annotations.rs index 7f5d74b3ee..34eada2988 100644 --- a/bindgen/ir/annotations.rs +++ b/bindgen/ir/annotations.rs @@ -172,7 +172,7 @@ impl Annotations { } /// The list of attributes that have been specified in this annotation. - pub(crate) fn attributes(&self) -> &[String] { + pub(crate) fn attributes(&self) -> &Vec { &self.attributes } From 4ae18542c5aed165f8a73bd21901c5d577523a48 Mon Sep 17 00:00:00 2001 From: oberrich Date: Wed, 5 Feb 2025 11:31:34 +0100 Subject: [PATCH 05/66] feat(attrs): add `merge_cfg_attributes` pass --- .../postprocessing/merge_cfg_attributes.rs | 255 ++++++++++++++++++ bindgen/codegen/postprocessing/mod.rs | 9 +- bindgen/options/mod.rs | 12 + 3 files changed, 274 insertions(+), 2 deletions(-) create mode 100644 bindgen/codegen/postprocessing/merge_cfg_attributes.rs diff --git a/bindgen/codegen/postprocessing/merge_cfg_attributes.rs b/bindgen/codegen/postprocessing/merge_cfg_attributes.rs new file mode 100644 index 0000000000..bf8cac9bf1 --- /dev/null +++ b/bindgen/codegen/postprocessing/merge_cfg_attributes.rs @@ -0,0 +1,255 @@ +use crate::HashMap; +use crate::HashSet; +use itertools::Itertools; +use proc_macro2::Span; +use quote::{quote, ToTokens}; +use syn::token::Unsafe; +use syn::Abi; +use syn::{ + Attribute, File, ForeignItem, Ident, Item, ItemConst, ItemEnum, ItemFn, + ItemForeignMod, ItemImpl, ItemMod, ItemStatic, ItemStruct, ItemType, + ItemUnion, ItemUse, +}; + +pub fn merge_cfg_attributes(file: &mut File) { + let mut visitor = Visitor::new(); + visitor.visit_file(file); +} + +struct SyntheticMod { + attrs: AttributeSet, + unsafety: Option, + abi: Option, + items: Vec, +} + +impl SyntheticMod { + pub fn new(attrs: AttributeSet) -> Self { + Self { + attrs, + unsafety: None, + abi: None, + items: vec![], + } + } +} + +#[derive(Default, Clone)] +struct AttributeSet { + cfg_attrs: HashSet, + cc_attrs: HashSet, + other_attrs: HashSet, + unsafety: Option, + abi: Option, +} + +impl AttributeSet { + fn new( + attrs: &[Attribute], + unsafety: Option, + abi: Option, + ) -> Self { + let mut attribute_set = AttributeSet::default(); + + for attr in attrs { + let target_set = if let Some(ident) = attr.path().get_ident() { + match ident.to_string().as_str() { + "cfg" => &mut attribute_set.cfg_attrs, + "link" => &mut attribute_set.cc_attrs, + _ => &mut attribute_set.other_attrs, + } + } else { + &mut attribute_set.other_attrs + }; + target_set.insert(attr.clone()); + } + attribute_set.unsafety = unsafety; + attribute_set.abi = abi; + + attribute_set + } + + fn extend( + &mut self, + attrs: &[Attribute], + unsafety: Option, + abi: Option, + ) { + let other = AttributeSet::new(attrs, unsafety, abi); + self.other_attrs.extend(other.other_attrs); + self.cfg_attrs.extend(other.cfg_attrs); + self.cc_attrs.extend(other.cc_attrs); + + self.unsafety = other.unsafety.or(self.unsafety); + self.abi = other.abi.or(self.abi.clone()); + } + + fn ident(&self) -> Ident { + Ident::new( + Itertools::intersperse( + self.unsafety + .map(|r#unsafe| r#unsafe.to_token_stream().to_string()) + .into_iter() + .chain( + self.abi + .as_ref() + .map(|abi| abi.to_token_stream().to_string()), + ) + .chain( + self.cfg_attrs + .iter() + .chain(self.cc_attrs.iter()) + .map(|attr| attr.to_token_stream().to_string()) + .sorted(), + ), + "_".to_string(), + ) + .collect::() + .replace(|c: char| !c.is_alphanumeric(), "_") + .chars() + .coalesce(|a, b| { + if a == '_' && b == '_' { + Ok(a) + } else { + Err((a, b)) + } + }) + .collect::() + .trim_matches('_'), + Span::call_site(), + ) + } +} + +struct Visitor { + synthetic_mods: HashMap, + new_items: Vec, +} + +impl Visitor { + fn new() -> Self { + Self { + synthetic_mods: HashMap::default(), + new_items: Vec::new(), + } + } + + fn visit_file(&mut self, file: &mut File) { + self.visit_items(&mut file.items); + + for ( + ref mut ident, + SyntheticMod { + ref mut attrs, + ref mut unsafety, + ref mut abi, + ref mut items, + }, + ) in self.synthetic_mods.drain() + { + let cfg_attrs = attrs.cfg_attrs.iter().collect::>(); + let cc_attrs = attrs.cc_attrs.iter().collect::>(); + let synthetic_mod = if abi.is_some() { + quote! { + #(#cfg_attrs)* + pub mod #ident { + #(#cc_attrs)* + #unsafety #abi { + #(#items)* + } + } + } + } else { + quote! { + #(#cfg_attrs)* + pub mod #ident { + #(#items)* + } + } + }; + + self.new_items.push(Item::Verbatim(quote! { + #synthetic_mod + + #(#cfg_attrs)* + pub use #ident::*; + })); + } + + file.items = std::mem::take(&mut self.new_items); + } + + fn visit_items(&mut self, items: &mut Vec) { + for mut item in std::mem::take(items) { + match &mut item { + Item::Const(ItemConst { ref mut attrs, .. }) | + Item::Struct(ItemStruct { ref mut attrs, .. }) | + Item::Enum(ItemEnum { ref mut attrs, .. }) | + Item::Union(ItemUnion { ref mut attrs, .. }) | + Item::Type(ItemType { ref mut attrs, .. }) | + Item::Use(ItemUse { ref mut attrs, .. }) | + Item::Static(ItemStatic { ref mut attrs, .. }) | + Item::Mod(ItemMod { ref mut attrs, .. }) | + Item::Impl(ItemImpl { ref mut attrs, .. }) | + Item::Fn(ItemFn { ref mut attrs, .. }) => { + let attr_set = AttributeSet::new(attrs, None, None); + *attrs = attr_set.other_attrs.iter().cloned().collect(); + self.insert_item_into_mod(attr_set, item); + } + Item::ForeignMod(foreign_mod) => { + self.visit_foreign_mod(foreign_mod); + } + _ => { + self.new_items.push(item); + } + } + } + } + + fn visit_foreign_mod(&mut self, foreign_mod: &mut ItemForeignMod) { + for mut foreign_item in std::mem::take(&mut foreign_mod.items) { + // When MSRV >= 1.79.0 we can return &Vec::new() in the generic case as this wll get lifetime extended, + // see also https://blog.rust-lang.org/2024/06/13/Rust-1.79.0.html#extending-automatic-temporary-lifetime-extension. + let mut _attrs = vec![]; + let (inner_attrs, inner_unsafety, inner_abi) = + match &mut foreign_item { + ForeignItem::Fn(f) => { + (&mut f.attrs, f.sig.unsafety, f.sig.abi.clone()) + } + ForeignItem::Static(s) => (&mut s.attrs, None, None), + ForeignItem::Type(t) => (&mut t.attrs, None, None), + ForeignItem::Macro(m) => (&mut m.attrs, None, None), + _ => (&mut _attrs, None, None), + }; + + let mut attr_set = AttributeSet::new( + &foreign_mod.attrs, + foreign_mod.unsafety, + Some(foreign_mod.abi.clone()), + ); + attr_set.extend(inner_attrs, inner_unsafety, inner_abi); + *inner_attrs = attr_set.other_attrs.iter().cloned().collect(); + + self.insert_item_into_mod( + attr_set, + Item::Verbatim(quote! { #foreign_item }), + ); + } + } + + fn insert_item_into_mod(&mut self, attr_set: AttributeSet, item: Item) { + if !attr_set.cfg_attrs.is_empty() || !attr_set.cc_attrs.is_empty() { + let ident = attr_set.ident(); + let synthetic_mod = self + .synthetic_mods + .entry(ident) + .or_insert_with(|| SyntheticMod::new(attr_set.clone())); + synthetic_mod.items.push(item); + synthetic_mod.unsafety = attr_set.unsafety; + synthetic_mod.abi = attr_set.abi.clone(); + synthetic_mod.attrs = attr_set; + } else { + self.new_items.push(item); + } + } +} \ No newline at end of file diff --git a/bindgen/codegen/postprocessing/mod.rs b/bindgen/codegen/postprocessing/mod.rs index 9641698521..15a5914f14 100644 --- a/bindgen/codegen/postprocessing/mod.rs +++ b/bindgen/codegen/postprocessing/mod.rs @@ -4,9 +4,11 @@ use syn::{parse2, File}; use crate::BindgenOptions; +mod merge_cfg_attributes; mod merge_extern_blocks; mod sort_semantically; +use merge_cfg_attributes::merge_cfg_attributes; use merge_extern_blocks::merge_extern_blocks; use sort_semantically::sort_semantically; @@ -26,8 +28,11 @@ macro_rules! pass { }; } -const PASSES: &[PostProcessingPass] = - &[pass!(merge_extern_blocks), pass!(sort_semantically)]; +const PASSES: &[PostProcessingPass] = &[ + pass!(merge_cfg_attributes), + pass!(merge_extern_blocks), + pass!(sort_semantically), +]; pub(crate) fn postprocessing( items: Vec, diff --git a/bindgen/options/mod.rs b/bindgen/options/mod.rs index 6bf652d4e1..ba7088147e 100644 --- a/bindgen/options/mod.rs +++ b/bindgen/options/mod.rs @@ -1985,6 +1985,18 @@ options! { }, as_args: "--merge-extern-blocks", }, + /// Whether to deduplicate `extern` blocks. + merge_cfg_attributes: bool { + methods: { + /// Merge all extern blocks under the same module into a single one. + /// + /// Extern blocks are not merged by default. + pub fn merge_cfg_attributes(mut self, doit: bool) -> Self { + self.options.merge_cfg_attributes = doit; + self + } + }, + as_args: "--merge-cfg-attributes", /// Whether to wrap unsafe operations in unsafe blocks. wrap_unsafe_ops: bool { methods: { From 1599556df935f41c5f40d7008621fef3451987f7 Mon Sep 17 00:00:00 2001 From: oberrich Date: Wed, 5 Feb 2025 11:33:14 +0100 Subject: [PATCH 06/66] doc(attrs): fix docs for `merge_cfg_attributes` pass --- bindgen/options/mod.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bindgen/options/mod.rs b/bindgen/options/mod.rs index ba7088147e..4e6579231e 100644 --- a/bindgen/options/mod.rs +++ b/bindgen/options/mod.rs @@ -1985,12 +1985,12 @@ options! { }, as_args: "--merge-extern-blocks", }, - /// Whether to deduplicate `extern` blocks. + /// Whether to deduplicate `cfg` attributes. merge_cfg_attributes: bool { methods: { - /// Merge all extern blocks under the same module into a single one. + /// Merge all unique combinations of cfg attributes into a module /// - /// Extern blocks are not merged by default. + /// Cfg attributes are not merged by default. pub fn merge_cfg_attributes(mut self, doit: bool) -> Self { self.options.merge_cfg_attributes = doit; self From 8542bfa4fd16bfb78c2df94c6d1a00b05589ca69 Mon Sep 17 00:00:00 2001 From: oberrich Date: Wed, 5 Feb 2025 11:34:50 +0100 Subject: [PATCH 07/66] fix: visibility of `FunctionKind` and `MethodKind` --- bindgen/ir/comp.rs | 2 +- bindgen/ir/function.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bindgen/ir/comp.rs b/bindgen/ir/comp.rs index 15f9cb4655..c24b951b29 100644 --- a/bindgen/ir/comp.rs +++ b/bindgen/ir/comp.rs @@ -32,7 +32,7 @@ pub(crate) enum CompKind { /// The kind of C++ method. #[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub(crate) enum MethodKind { +pub enum MethodKind { /// A constructor. We represent it as method for convenience, to avoid code /// duplication. Constructor, diff --git a/bindgen/ir/function.rs b/bindgen/ir/function.rs index 83b748a5f4..cdf5c59cd1 100644 --- a/bindgen/ir/function.rs +++ b/bindgen/ir/function.rs @@ -19,7 +19,7 @@ const RUST_DERIVE_FUNPTR_LIMIT: usize = 12; /// What kind of function are we looking at? #[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub(crate) enum FunctionKind { +pub enum FunctionKind { /// A plain, free function. Function, /// A method of some kind. From 2878dbe1b1ab6ddf4919c0506eb34f46e268f085 Mon Sep 17 00:00:00 2001 From: oberrich Date: Wed, 5 Feb 2025 11:48:17 +0100 Subject: [PATCH 08/66] feat(attrs): implement cli --- bindgen/callbacks.rs | 3 -- bindgen/lib.rs | 6 +++- bindgen/options/cli.rs | 64 ++++++++++++++++++++++++++++-------------- bindgen/options/mod.rs | 1 + 4 files changed, 49 insertions(+), 25 deletions(-) diff --git a/bindgen/callbacks.rs b/bindgen/callbacks.rs index 1d166f5391..2b584311b7 100644 --- a/bindgen/callbacks.rs +++ b/bindgen/callbacks.rs @@ -232,11 +232,8 @@ pub struct DeriveInfo<'a> { #[derive(Debug)] #[non_exhaustive] pub struct AttributeInfo<'a> { - /// The name of the type. /// The name of the item. pub name: &'a str, - /// The kind of the type. - pub kind: TypeKind, /// The kind of the item. pub kind: AttributeItemKind, } diff --git a/bindgen/lib.rs b/bindgen/lib.rs index 0a8f29d158..a3cb78492e 100644 --- a/bindgen/lib.rs +++ b/bindgen/lib.rs @@ -86,7 +86,7 @@ pub const DEFAULT_ANON_FIELDS_PREFIX: &str = "__bindgen_anon_"; const DEFAULT_NON_EXTERN_FNS_SUFFIX: &str = "__extern"; fn file_is_cpp(name_file: &str) -> bool { - Path::new(name_file).extension().map_or(false, |ext| { + Path::new(name_file).extension().is_some_and(|ext| { ext.eq_ignore_ascii_case("hpp") || ext.eq_ignore_ascii_case("hxx") || ext.eq_ignore_ascii_case("hh") || @@ -579,6 +579,10 @@ impl BindgenOptions { fn for_each_callback(&self, f: impl Fn(&dyn callbacks::ParseCallbacks)) { self.parse_callbacks.iter().for_each(|cb| f(cb.as_ref())); } + + fn for_each_callback_mut(&self, mut f: impl FnMut(&dyn callbacks::ParseCallbacks)) { + self.parse_callbacks.iter().for_each(|cb| f(cb.as_ref())); + } fn process_comment(&self, comment: &str) -> String { let comment = comment::preprocess(comment); diff --git a/bindgen/options/cli.rs b/bindgen/options/cli.rs index 8c4c05bc84..287d781375 100644 --- a/bindgen/options/cli.rs +++ b/bindgen/options/cli.rs @@ -1,15 +1,10 @@ #![allow(unused_qualifications)] // Clap somehow generates a lot of these use crate::{ - builder, - callbacks::{ - AttributeInfo, DeriveInfo, ItemInfo, ParseCallbacks, TypeKind, - }, - features::{RustEdition, EARLIEST_STABLE_RUST}, - regex_set::RegexSet, - Abi, AliasVariation, Builder, CodegenConfig, EnumVariation, - FieldVisibilityKind, Formatter, MacroTypeVariation, NonCopyUnionStyle, - RustTarget, + builder, callbacks::{ + AttributeInfo, AttributeItemKind, DeriveInfo, ItemInfo, ParseCallbacks, + TypeKind, + }, features::{RustEdition, EARLIEST_STABLE_RUST}, ir::function::FunctionKind, regex_set::RegexSet, Abi, AliasVariation, Builder, CodegenConfig, EnumVariation, FieldVisibilityKind, Formatter, HashSet, MacroTypeVariation, NonCopyUnionStyle, RustTarget }; use clap::{ error::{Error, ErrorKind}, @@ -477,7 +472,7 @@ struct BindgenCommand { /// Derive custom traits on a `union`. The CUSTOM value must be of the shape REGEX=DERIVE where DERIVE is a coma-separated list of derive macros. #[arg(long, value_name = "CUSTOM", value_parser = parse_custom_derive)] with_derive_custom_union: Vec<(Vec, String)>, - /// Add custom attributes on any kind of type. The CUSTOM value must be of the shape REGEX=ATTRIBUTE where ATTRIBUTE is a coma-separated list of attributes. + /// Add custom attributes on any item. The CUSTOM value must be of the shape REGEX=ATTRIBUTE where ATTRIBUTE is a coma-separated list of attributes. #[arg(long, value_name = "CUSTOM", value_parser = parse_custom_attribute)] with_attribute_custom: Vec<(Vec, String)>, /// Add custom attributes on a `struct`. The CUSTOM value must be of the shape REGEX=ATTRIBUTE where ATTRIBUTE is a coma-separated list of attributes. @@ -489,6 +484,12 @@ struct BindgenCommand { /// Add custom attributes on a `union`. The CUSTOM value must be of the shape REGEX=ATTRIBUTE where ATTRIBUTE is a coma-separated list of attributes. #[arg(long, value_name = "CUSTOM", value_parser = parse_custom_attribute)] with_attribute_custom_union: Vec<(Vec, String)>, + /// Add custom attributes on a variable. The CUSTOM value must be of the shape REGEX=ATTRIBUTE where ATTRIBUTE is a coma-separated list of attributes. + #[arg(long, value_name = "CUSTOM", value_parser = parse_custom_attribute)] + with_attribute_custom_var: Vec<(Vec, String)>, + /// Add custom attributes on an `fn`. The CUSTOM value must be of the shape REGEX=ATTRIBUTE where ATTRIBUTE is a coma-separated list of attributes. + #[arg(long, value_name = "CUSTOM", value_parser = parse_custom_attribute)] + with_attribute_custom_function: Vec<(Vec, String)>, /// Generate wrappers for `static` and `static inline` functions. #[arg(long)] wrap_static_fns: bool, @@ -645,6 +646,8 @@ where with_attribute_custom_struct, with_attribute_custom_enum, with_attribute_custom_union, + with_attribute_custom_var, + with_attribute_custom_function, wrap_static_fns, wrap_static_fns_path, wrap_static_fns_suffix, @@ -745,7 +748,7 @@ where #[derive(Debug)] struct CustomAttributeCallback { attributes: Vec, - kind: Option, + kind: Option, regex_set: RegexSet, } @@ -755,10 +758,20 @@ where let flag = match &self.kind { None => "--with-attribute-custom", - Some(TypeKind::Struct) => "--with-attribute-custom-struct", - Some(TypeKind::Enum) => "--with-attribute-custom-enum", - Some(TypeKind::Union) => "--with-attribute-custom-union", - }; + Some(AttributeItemKind::Struct) => { + "--with-attribute-custom-struct" + } + Some(AttributeItemKind::Enum) => "--with-attribute-custom-enum", + Some(AttributeItemKind::Union) => { + "--with-attribute-custom-union" + } + Some(AttributeItemKind::Var) => { + "--with-attribute-custom-var" + } + Some(AttributeItemKind::Function(_)) => { + "--with-attribute-custom-function" + } + }; let attributes = self.attributes.join(","); @@ -772,13 +785,12 @@ where args } - fn add_attributes(&self, info: &AttributeInfo<'_>) -> Vec { + fn process_attributes(&self, info: &AttributeInfo<'_>, attrs: &mut HashSet) { if self.kind.map_or(true, |kind| kind == info.kind) && self.regex_set.matches(info.name) { - return self.attributes.clone(); + attrs.extend(self.attributes.clone()); } - vec![] } } @@ -1010,19 +1022,29 @@ where (with_attribute_custom, None, "--with-attribute-custom"), ( with_attribute_custom_struct, - Some(TypeKind::Struct), + Some(AttributeItemKind::Struct), "--with-attribute-custom-struct", ), ( with_attribute_custom_enum, - Some(TypeKind::Enum), + Some(AttributeItemKind::Enum), "--with-attribute-custom-enum", ), ( with_attribute_custom_union, - Some(TypeKind::Union), + Some(AttributeItemKind::Union), "--with-attribute-custom-union", ), + ( + with_attribute_custom_function, + Some(AttributeItemKind::Var), + "--with-attribute-custom-var", + ), + ( + with_attribute_custom_function, + Some(AttributeItemKind::Function(FunctionKind::Function)), + "--with-attribute-custom-function", + ), ] { #[cfg(feature = "experimental")] let name = emit_diagnostics.then_some(_name); diff --git a/bindgen/options/mod.rs b/bindgen/options/mod.rs index 4e6579231e..bd2b768366 100644 --- a/bindgen/options/mod.rs +++ b/bindgen/options/mod.rs @@ -1997,6 +1997,7 @@ options! { } }, as_args: "--merge-cfg-attributes", + }, /// Whether to wrap unsafe operations in unsafe blocks. wrap_unsafe_ops: bool { methods: { From d12cce9bc961b29099d354cc1cea0e541fa8341e Mon Sep 17 00:00:00 2001 From: oberrich Date: Wed, 5 Feb 2025 13:11:49 +0100 Subject: [PATCH 09/66] feat(attrs): use `Vec` instead of `HashSet` --- bindgen/callbacks.rs | 11 ++++++++++- bindgen/codegen/mod.rs | 15 ++++++++------- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/bindgen/callbacks.rs b/bindgen/callbacks.rs index 2b584311b7..7c4c6d8fb2 100644 --- a/bindgen/callbacks.rs +++ b/bindgen/callbacks.rs @@ -129,8 +129,17 @@ pub trait ParseCallbacks: fmt::Debug { vec![] } + /// Provide a list of custom attributes. + /// + /// If no additional attributes are wanted, this function should return an + /// empty `Vec`. + // TODO: Call this process_attributes function in codegen/mod.rs + fn add_attributes(&self, _info: &AttributeInfo<'_>) -> Vec { + vec![] + } + /// Process an item's attribute - fn process_attributes(&self, _info: &AttributeInfo<'_>, _attributes: &mut HashSet) {} + fn process_attributes(&self, _info: &AttributeInfo<'_>, _attributes: &mut Vec) {} /// Process a source code comment. fn process_comment(&self, _comment: &str) -> Option { diff --git a/bindgen/codegen/mod.rs b/bindgen/codegen/mod.rs index 0f284fdc52..f2f0834a20 100644 --- a/bindgen/codegen/mod.rs +++ b/bindgen/codegen/mod.rs @@ -233,7 +233,7 @@ where .collect() } -fn normalize_attributes(attrs: HashSet) -> HashSet { +fn normalize_attributes(attrs: Vec) -> Vec { attrs .iter() .map(|attr| { @@ -261,7 +261,7 @@ fn process_attributes( result: &mut CodegenResult, item: &Item, ctx: &BindgenContext, - attrs: HashSet, + attrs: Vec, kind: AttributeItemKind, ) -> Vec { // TODO: attrs.extend(parse_tokens(item.annotations().attributes())); @@ -338,7 +338,7 @@ struct CodegenResult<'a> { items_to_serialize: Vec<(ItemId, Option)>, /// Tracks attributes of items during codegen - item_attributes: HashMap>, + item_attributes: HashMap>, } impl<'a> CodegenResult<'a> { @@ -361,14 +361,14 @@ impl<'a> CodegenResult<'a> { } } - fn set_attributes(&mut self, item_id: ItemId, attributes: HashSet) { + fn set_attributes(&mut self, item_id: ItemId, attributes: Vec) { *self .item_attributes .entry(item_id) - .or_insert_with(HashSet::default) = attributes; + .or_insert_with(Default::default) = attributes; } - fn get_attributes(&self, item_id: ItemId) -> Option<&HashSet> { + fn get_attributes(&self, item_id: ItemId) -> Option<&Vec> { self.item_attributes.get(&item_id) } @@ -1117,7 +1117,8 @@ impl CodeGenerator for Type { attrs.insert(attributes::doc(&comment).to_string()); } - let mut attrs = HashSet::default(); + let mut attrs = vec![]; + if let Some(inner_attrs) = result.get_attributes(inner_item.id()) { From 48b85997dd31e3c7a9184353b94b70ffaaf6f49a Mon Sep 17 00:00:00 2001 From: oberrich Date: Thu, 6 Feb 2025 07:43:43 +0100 Subject: [PATCH 10/66] feat(attrs): format attribute tokens using config --- bindgen/Cargo.toml | 2 +- bindgen/callbacks.rs | 9 +- bindgen/codegen/mod.rs | 237 ++++++++++-------- .../postprocessing/merge_cfg_attributes.rs | 2 +- bindgen/ir/comment.rs | 6 +- bindgen/lib.rs | 173 ++++++------- bindgen/options/cli.rs | 23 +- 7 files changed, 243 insertions(+), 209 deletions(-) diff --git a/bindgen/Cargo.toml b/bindgen/Cargo.toml index c01f8f0c44..6cbc084063 100644 --- a/bindgen/Cargo.toml +++ b/bindgen/Cargo.toml @@ -16,7 +16,7 @@ readme = "../README.md" repository = "https://github.com/rust-lang/rust-bindgen" documentation = "https://docs.rs/bindgen" homepage = "https://rust-lang.github.io/rust-bindgen/" -version = "0.71.1" +version = "0.69.5" build = "build.rs" rust-version.workspace = true edition.workspace = true diff --git a/bindgen/callbacks.rs b/bindgen/callbacks.rs index 7c4c6d8fb2..834d8d5a46 100644 --- a/bindgen/callbacks.rs +++ b/bindgen/callbacks.rs @@ -3,6 +3,7 @@ pub use crate::ir::analysis::DeriveTrait; pub use crate::ir::derive::CanDerive as ImplementsTrait; pub use crate::ir::enum_ty::{EnumVariantCustomBehavior, EnumVariantValue}; +pub use crate::ir::function::FunctionKind; pub use crate::ir::int::IntKind; use std::fmt; @@ -139,7 +140,12 @@ pub trait ParseCallbacks: fmt::Debug { } /// Process an item's attribute - fn process_attributes(&self, _info: &AttributeInfo<'_>, _attributes: &mut Vec) {} + fn process_attributes( + &self, + _info: &AttributeInfo<'_>, + _attributes: &mut Vec, + ) { + } /// Process a source code comment. fn process_comment(&self, _comment: &str) -> Option { @@ -260,7 +266,6 @@ pub enum AttributeItemKind { Var, /// The item is a Rust `fn`. Function(FunctionKind), - // TODO: Add `Alias` variant and more information about other items } #[derive(Debug, Clone, Copy, PartialEq, Eq)] diff --git a/bindgen/codegen/mod.rs b/bindgen/codegen/mod.rs index f2f0834a20..3e2257ffd4 100644 --- a/bindgen/codegen/mod.rs +++ b/bindgen/codegen/mod.rs @@ -25,6 +25,7 @@ use crate::callbacks::{ DiscoveredItemId, FieldInfo, TypeKind as DeriveTypeKind, }; use crate::codegen::error::Error; +use crate::comment::{self, preprocess}; use crate::ir::analysis::{HasVtable, Sizedness}; use crate::ir::annotations::{ Annotations, FieldAccessorKind, FieldVisibilityKind, @@ -55,19 +56,23 @@ use crate::ir::template::{ use crate::ir::ty::{Type, TypeKind}; use crate::ir::var::Var; -use proc_macro2::{Ident, Span, TokenStream}; -use quote::{ToTokens, TokenStreamExt}; -use syn::parse::ParseStream; -use syn::token::Pound; -use syn::{parse, parse2, parse_quote, parse_str, Attribute, Block}; - -use crate::{Entry, HashMap, HashSet}; +use crate::quote::{ToTokens, TokenStreamExt}; +use itertools::Itertools; +use proc_macro2::{Group, Ident, Span, TokenStream, TokenTree}; +use syn::meta::ParseNestedMeta; +use syn::parse::{ParseBuffer, ParseStream}; +use syn::{ + parse, parse_quote, Attribute, Expr, ForeignItemMacro, Lit, Meta, MetaList, + MetaNameValue, +}; + +use crate::{format_tokens, Entry, HashMap, HashSet}; use std::borrow::Cow; use std::cell::Cell; use std::collections::VecDeque; use std::ffi::CStr; use std::fmt::{self, Write}; -use std::ops::{self, Deref}; +use std::ops::{Deref, DerefMut}; use std::str::{self, FromStr}; #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -96,10 +101,7 @@ impl fmt::Display for CodegenError { // Name of type defined in constified enum module pub(crate) static CONSTIFIED_ENUM_MODULE_REPR_NAME: &str = "Type"; -fn top_level_path( - ctx: &BindgenContext, - item: &Item, -) -> Vec { +fn top_level_path(ctx: &BindgenContext, item: &Item) -> Vec { let mut path = vec![quote! { self }]; if ctx.options().enable_cxx_namespaces { @@ -111,10 +113,7 @@ fn top_level_path( path } -fn root_import( - ctx: &BindgenContext, - module: &Item, -) -> proc_macro2::TokenStream { +fn root_import(ctx: &BindgenContext, module: &Item) -> TokenStream { assert!(ctx.options().enable_cxx_namespaces, "Somebody messed it up"); assert!(module.is_module()); @@ -233,51 +232,87 @@ where .collect() } -fn normalize_attributes(attrs: Vec) -> Vec { - attrs - .iter() - .map(|attr| { - let mut in_quotes = None; - attr.chars() - .filter_map(|c| match c { - '"' | '\'' if in_quotes.is_none() => { - in_quotes = Some(c); - Some(c) - } - c if in_quotes == Some(c) => { - in_quotes = None; - Some(c) - } - c if in_quotes.is_some() => Some(c), - c if !c.is_whitespace() => Some(c), - _ => None, - }) - .collect() - }) - .collect() -} - fn process_attributes( result: &mut CodegenResult, item: &Item, ctx: &BindgenContext, - attrs: Vec, + mut attrs: Vec, kind: AttributeItemKind, ) -> Vec { - // TODO: attrs.extend(parse_tokens(item.annotations().attributes())); + attrs.extend(parse_tokens(item.annotations().attributes())); + + let mut attrs_out = vec![]; + if !attrs.is_empty() && attrs.iter().any(|attr| !attr.is_empty()) { + // If this ever errors, we may have to - depending on the attributes - use different dummy items to + // attach the attributes to. This is necessary to get a valid unparse from prettyplease/rustfmt + let attrs_with_body = quote! { + #(#attrs)* + fn body() {} + }; + + let mut comments = vec![]; + let mut block_comment = false; + + // TODO: It might be a good idea to just default to prettyplease::unparse here + // TODO: as the user may have `None` in their config and `Rustfmt` invocations can + // TODO: become quite expensive when ran against each attribute. + for line in format_tokens(ctx.options(), &attrs_with_body) + .expect("can format attrs") + .split('\n') + .take_while(|line| !line.starts_with("fn body")) + .join("\n") + .lines() + { + let trimmed = line.trim(); + if trimmed.starts_with("/*") { + block_comment = true; + } + + let cleaned = trimmed + .trim_start_matches('/') + .trim_start_matches('*') + .trim_start_matches('!') + .trim_end_matches('/') + .trim_end_matches('*'); + + if block_comment || + trimmed.starts_with("///") || + trimmed.starts_with("//") + { + comments.push(cleaned.to_string()); + } else if trimmed.starts_with('#') { + attrs_out.push(line.into()); + } + + if trimmed.ends_with("*/") { + block_comment = false; + } + } + + if !comments.is_empty() { + attrs_out.push(format!("#[doc = \"{}\"]", comments.join("\n"))); + } - let mut attrs = normalize_attributes(attrs); + //println!("cargo:warning=attrs: {attrs_out:?}"); + } + + // TODO: Call add_attributes and renormalize attributes ctx.options().for_each_callback_mut(|cb| { cb.process_attributes( &AttributeInfo { name: &item.canonical_name(ctx), kind, }, - &mut attrs, + &mut attrs_out, ); }); - result.set_attributes(item.id(), attrs.clone()); - parse_tokens(normalize_attributes(attrs)) + + // TODO: Store as token stream here + result.set_attributes( + item.id(), + attrs_out.iter().map(|t| t.to_string()).collect(), + ); + parse_tokens(attrs_out) } struct WrapAsVariadic { @@ -286,7 +321,7 @@ struct WrapAsVariadic { } struct CodegenResult<'a> { - items: Vec, + items: Vec, dynamic_items: DynamicItems, /// A monotonic counter used to add stable unique ID's to stuff that doesn't @@ -430,7 +465,7 @@ impl<'a> CodegenResult<'a> { self.vars_seen.insert(name.into()); } - fn inner(&mut self, cb: F) -> Vec + fn inner(&mut self, cb: F) -> Vec where F: FnOnce(&mut Self), { @@ -448,15 +483,15 @@ impl<'a> CodegenResult<'a> { } } -impl ops::Deref for CodegenResult<'_> { - type Target = Vec; +impl Deref for CodegenResult<'_> { + type Target = Vec; fn deref(&self) -> &Self::Target { &self.items } } -impl ops::DerefMut for CodegenResult<'_> { +impl DerefMut for CodegenResult<'_> { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.items } @@ -687,9 +722,7 @@ impl CodeGenerator for Module { if let Some(raw_lines) = ctx.options().module_lines.get(&path) { for raw_line in raw_lines { found_any = true; - result.push( - TokenStream::from_str(raw_line).unwrap(), - ); + result.push(TokenStream::from_str(raw_line).unwrap()); } } @@ -755,7 +788,7 @@ impl CodeGenerator for Var { attrs.push(attributes::doc(&comment)); } - attrs.extend(item.annotations().attributes()); + attrs.extend(parse_tokens(item.annotations().attributes())); let var_ty = self.ty(); let ty = var_ty.to_rust_ty_or_opaque(ctx, &()); @@ -1110,15 +1143,15 @@ impl CodeGenerator for Type { } else { ctx.options().default_alias_style }; - - attrs.extend(item.annotations().attributes().iter().cloned()); - + + let mut attrs = vec![]; + + attrs.extend(parse_tokens(item.annotations().attributes())); + if let Some(comment) = item.comment(ctx) { - attrs.insert(attributes::doc(&comment).to_string()); + attrs.push(attributes::doc(&comment)); } - let mut attrs = vec![]; - if let Some(inner_attrs) = result.get_attributes(inner_item.id()) { @@ -1132,7 +1165,7 @@ impl CodeGenerator for Type { if attr.path().is_ident("cfg") || attr.path().is_ident("link") { - Some(attr.to_token_stream().to_string()) + Some(attr.to_token_stream()) } else { None } @@ -1197,9 +1230,9 @@ impl CodeGenerator for Type { attrs.push(attributes::derives(&derives)); // TODO: Add custom attributes from callback here - + quote! { - #( #attributes )* + #( #attrs )* pub struct #rust_name } } @@ -1262,7 +1295,7 @@ impl CodeGenerator for Type { if alias_style == AliasVariation::NewTypeDeref { let prefix = ctx.trait_prefix(); tokens.append_all(quote! { - #( #cfg_attrs )* + #( #attrs )* impl ::#prefix::ops::Deref for #rust_name { type Target = #inner_rust_type; #[inline] @@ -1270,7 +1303,7 @@ impl CodeGenerator for Type { &self.0 } } - #( #cfg_attrs )* + #( #attrs )* impl ::#prefix::ops::DerefMut for #rust_name { #[inline] fn deref_mut(&mut self) -> &mut Self::Target { @@ -1757,7 +1790,7 @@ impl FieldCodegen<'_> for FieldData { impl BitfieldUnit { /// Get the constructor name for this bitfield unit. - fn ctor_name(&self) -> proc_macro2::TokenStream { + fn ctor_name(&self) -> TokenStream { let ctor_name = Ident::new( &format!("new_bitfield_{}", self.nth()), Span::call_site(), @@ -1775,9 +1808,9 @@ impl Bitfield { fn extend_ctor_impl( &self, ctx: &BindgenContext, - param_name: &proc_macro2::TokenStream, - mut ctor_impl: proc_macro2::TokenStream, - ) -> proc_macro2::TokenStream { + param_name: &TokenStream, + mut ctor_impl: TokenStream, + ) -> TokenStream { let bitfield_ty = ctx.resolve_type(self.ty()); let bitfield_ty_layout = bitfield_ty .layout(ctx) @@ -1808,9 +1841,7 @@ impl Bitfield { } } -fn access_specifier( - visibility: FieldVisibilityKind, -) -> proc_macro2::TokenStream { +fn access_specifier(visibility: FieldVisibilityKind) -> TokenStream { match visibility { FieldVisibilityKind::Private => quote! {}, FieldVisibilityKind::PublicCrate => quote! { pub(crate) }, @@ -2644,11 +2675,9 @@ impl CodeGenerator for CompInfo { } if item.must_use(ctx) { - attrs.insert(attributes::must_use().to_string()); + attrs.push(attributes::must_use()); } - attrs.extend(item.annotations().attributes().iter().cloned()); - let attrs = process_attributes( result, item, @@ -2658,16 +2687,17 @@ impl CodeGenerator for CompInfo { AttributeItemKind::Union } else { AttributeItemKind::Struct - } + }, ); - + let cfg_attrs = attrs .iter() .filter(|t| !t.is_empty()) .map(|t| parse_quote! {#t}) .filter(|attr: &Attribute| { attr.path().is_ident("cfg") || attr.path().is_ident("link") - }); + }) + .collect_vec(); let mut tokens = if is_rust_union { quote! { @@ -2986,10 +3016,10 @@ impl CompInfo { &self, ctx: &BindgenContext, canonical_ident: &Ident, - flex_inner_ty: Option<&proc_macro2::TokenStream>, + flex_inner_ty: Option<&TokenStream>, generic_param_names: &[Ident], - impl_generics_labels: &proc_macro2::TokenStream, - ) -> proc_macro2::TokenStream { + impl_generics_labels: &TokenStream, + ) -> TokenStream { let prefix = ctx.trait_prefix(); let flex_array = flex_inner_ty.as_ref().map(|ty| quote! { [ #ty ] }); @@ -3416,8 +3446,6 @@ enum EnumBuilder<'a> { }, Consts { variants: Vec, - // TODO: Investigate if we end up using this - attrs: Vec, }, ModuleConsts { module_name: &'a str, @@ -3479,7 +3507,7 @@ impl<'a> EnumBuilder<'a> { }); } - EnumBuilder::Consts { variants, attrs } + EnumBuilder::Consts { variants } } EnumVariation::ModuleConsts => { @@ -3495,7 +3523,7 @@ impl<'a> EnumBuilder<'a> { EnumBuilder::ModuleConsts { module_name: name, module_items: vec![type_definition], - attrs + attrs, } } } @@ -3582,7 +3610,7 @@ impl<'a> EnumBuilder<'a> { self } - EnumBuilder::Consts { ref attrs, .. } => { + EnumBuilder::Consts { .. } => { let constant_name = match mangling_prefix { Some(prefix) => { Cow::Owned(format!("{prefix}_{variant_name}")) @@ -3601,7 +3629,7 @@ impl<'a> EnumBuilder<'a> { EnumBuilder::ModuleConsts { module_name, mut module_items, - attrs + attrs, } => { let name = ctx.rust_ident(variant_name); let ty = ctx.rust_ident(CONSTIFIED_ENUM_MODULE_REPR_NAME); @@ -3613,7 +3641,7 @@ impl<'a> EnumBuilder<'a> { EnumBuilder::ModuleConsts { module_name, module_items, - attrs + attrs, } } } @@ -3860,22 +3888,15 @@ impl CodeGenerator for Enum { // In most cases this will be a no-op, since custom_derives will be empty. derives.extend(custom_derives.iter().map(|s| s.as_str())); - // TODO: Try and make sure this makes sense attrs.extend(item.annotations().attributes()); - attrs.extend( - item.annotations() - .attributes() - .iter() - .map(|s| s.parse().unwrap()), - ); - let attrs = process_attributes( + attrs.push(attributes::derives(&derives)); + + attrs = process_attributes( result, item, ctx, attrs, AttributeItemKind::Enum, ); - - attrs.push(attributes::derives(&derives)); } fn add_constant( @@ -3911,18 +3932,15 @@ impl CodeGenerator for Enum { let repr = repr.to_rust_ty_or_opaque(ctx, item); let has_typedef = ctx.is_enum_typedef_combo(item.id()); - let mut builder = - EnumBuilder::new(&name, attrs, &repr, variation, has_typedef); - - // The custom attribute callback may return a list of attributes; + // The custom attribute callback may return a list of attributes; // add them to the end of the list. - attrs.extend(item.annotations().attributes().iter().cloned()); + attrs.extend(parse_tokens(item.annotations().attributes().clone())); let attrs = process_attributes( result, item, ctx, - attrs, + attrs.clone(), AttributeItemKind::Enum, ); @@ -4470,8 +4488,7 @@ impl TryToRustTy for Type { } TypeKind::Enum(..) => { let path = item.namespace_aware_canonical_path(ctx); - let path = TokenStream::from_str(&path.join("::")) - .unwrap(); + let path = TokenStream::from_str(&path.join("::")).unwrap(); Ok(syn::parse_quote!(#path)) } TypeKind::TemplateInstantiation(ref inst) => { @@ -4757,7 +4774,7 @@ impl CodeGenerator for Function { let mut attrs = vec![]; attrs.extend(parse_tokens(item.annotations().attributes())); - + let must_use = signature.must_use() || { let ret_ty = signature .return_type() @@ -4878,7 +4895,7 @@ impl CodeGenerator for Function { .rust_features .unsafe_extern_blocks .then(|| quote!(unsafe)); - + let attrs = process_attributes( result, item, @@ -4919,7 +4936,7 @@ impl CodeGenerator for Function { &args_identifiers, &ret, &ret_ty, - &attributes, + &attrs, ctx, ); } else { diff --git a/bindgen/codegen/postprocessing/merge_cfg_attributes.rs b/bindgen/codegen/postprocessing/merge_cfg_attributes.rs index bf8cac9bf1..663be607e9 100644 --- a/bindgen/codegen/postprocessing/merge_cfg_attributes.rs +++ b/bindgen/codegen/postprocessing/merge_cfg_attributes.rs @@ -252,4 +252,4 @@ impl Visitor { self.new_items.push(item); } } -} \ No newline at end of file +} diff --git a/bindgen/ir/comment.rs b/bindgen/ir/comment.rs index a4ba320186..49828dd4e3 100644 --- a/bindgen/ir/comment.rs +++ b/bindgen/ir/comment.rs @@ -2,7 +2,7 @@ /// The type of a comment. #[derive(Debug, PartialEq, Eq)] -enum Kind { +pub(crate) enum Kind { /// A `///` comment, or something of the like. /// All lines in a comment should start with the same symbol. SingleLines, @@ -21,7 +21,7 @@ pub(crate) fn preprocess(comment: &str) -> String { } /// Gets the kind of the doc comment, if it is one. -fn kind(comment: &str) -> Option { +pub(crate) fn kind(comment: &str) -> Option { if comment.starts_with("/*") { Some(Kind::MultiLine) } else if comment.starts_with("//") { @@ -44,7 +44,7 @@ fn preprocess_single_lines(comment: &str) -> String { lines.join("\n") } -fn preprocess_multi_line(comment: &str) -> String { +pub(crate) fn preprocess_multi_line(comment: &str) -> String { let comment = comment .trim_start_matches('/') .trim_end_matches('/') diff --git a/bindgen/lib.rs b/bindgen/lib.rs index a3cb78492e..2db7a0a6fe 100644 --- a/bindgen/lib.rs +++ b/bindgen/lib.rs @@ -579,8 +579,11 @@ impl BindgenOptions { fn for_each_callback(&self, f: impl Fn(&dyn callbacks::ParseCallbacks)) { self.parse_callbacks.iter().for_each(|cb| f(cb.as_ref())); } - - fn for_each_callback_mut(&self, mut f: impl FnMut(&dyn callbacks::ParseCallbacks)) { + + fn for_each_callback_mut( + &self, + mut f: impl FnMut(&dyn callbacks::ParseCallbacks), + ) { self.parse_callbacks.iter().for_each(|cb| f(cb.as_ref())); } @@ -942,7 +945,7 @@ impl Bindings { writer.write_all(NL.as_bytes())?; } - match self.format_tokens(&self.module) { + match format_tokens(&self.options, &self.module) { Ok(formatted_bindings) => { writer.write_all(formatted_bindings.as_bytes())?; } @@ -955,102 +958,102 @@ impl Bindings { } Ok(()) } +} - /// Gets the rustfmt path to rustfmt the generated bindings. - fn rustfmt_path(&self) -> io::Result> { - debug_assert!(matches!(self.options.formatter, Formatter::Rustfmt)); - if let Some(ref p) = self.options.rustfmt_path { - return Ok(Cow::Borrowed(p)); - } - if let Ok(rustfmt) = env::var("RUSTFMT") { - return Ok(Cow::Owned(rustfmt.into())); - } - // No rustfmt binary was specified, so assume that the binary is called - // "rustfmt" and that it is in the user's PATH. - Ok(Cow::Owned("rustfmt".into())) +/// Gets the rustfmt path to rustfmt the generated bindings. +pub(crate) fn rustfmt_path( + options: &BindgenOptions, +) -> io::Result> { + debug_assert!(matches!(options.formatter, Formatter::Rustfmt)); + if let Some(ref p) = options.rustfmt_path { + return Ok(Cow::Borrowed(p)); } + if let Ok(rustfmt) = env::var("RUSTFMT") { + return Ok(Cow::Owned(rustfmt.into())); + } + // No rustfmt binary was specified, so assume that the binary is called + // "rustfmt" and that it is in the user's PATH. + Ok(Cow::Owned("rustfmt".into())) +} - /// Formats a token stream with the formatter set up in `BindgenOptions`. - fn format_tokens( - &self, - tokens: &proc_macro2::TokenStream, - ) -> io::Result { - let _t = time::Timer::new("rustfmt_generated_string") - .with_output(self.options.time_phases); - - match self.options.formatter { - Formatter::None => return Ok(tokens.to_string()), - #[cfg(feature = "prettyplease")] - Formatter::Prettyplease => { - return Ok(prettyplease::unparse(&syn::parse_quote!(#tokens))); - } - Formatter::Rustfmt => (), +/// Formats a token stream with the formatter set up in `BindgenOptions`. +pub(crate) fn format_tokens( + options: &BindgenOptions, + tokens: &proc_macro2::TokenStream, +) -> io::Result { + let _t = time::Timer::new("rustfmt_generated_string") + .with_output(options.time_phases); + + match options.formatter { + Formatter::None => return Ok(tokens.to_string()), + #[cfg(feature = "prettyplease")] + Formatter::Prettyplease => { + return Ok(prettyplease::unparse(&syn::parse_quote!(#tokens))); } + Formatter::Rustfmt => (), + } - let rustfmt = self.rustfmt_path()?; - let mut cmd = Command::new(&*rustfmt); + let rustfmt = rustfmt_path(&options)?; + let mut cmd = Command::new(&*rustfmt); - cmd.stdin(Stdio::piped()).stdout(Stdio::piped()); + cmd.stdin(Stdio::piped()).stdout(Stdio::piped()); - if let Some(path) = self - .options - .rustfmt_configuration_file - .as_ref() - .and_then(|f| f.to_str()) - { - cmd.args(["--config-path", path]); - } + if let Some(path) = options + .rustfmt_configuration_file + .as_ref() + .and_then(|f| f.to_str()) + { + cmd.args(["--config-path", path]); + } - let edition = self - .options - .rust_edition - .unwrap_or_else(|| self.options.rust_target.latest_edition()); - cmd.args(["--edition", &format!("{edition}")]); + let edition = options + .rust_edition + .unwrap_or_else(|| options.rust_target.latest_edition()); + cmd.args(["--edition", &format!("{edition}")]); - let mut child = cmd.spawn()?; - let mut child_stdin = child.stdin.take().unwrap(); - let mut child_stdout = child.stdout.take().unwrap(); + let mut child = cmd.spawn()?; + let mut child_stdin = child.stdin.take().unwrap(); + let mut child_stdout = child.stdout.take().unwrap(); - let source = tokens.to_string(); + let source = tokens.to_string(); - // Write to stdin in a new thread, so that we can read from stdout on this - // thread. This keeps the child from blocking on writing to its stdout which - // might block us from writing to its stdin. - let stdin_handle = ::std::thread::spawn(move || { - let _ = child_stdin.write_all(source.as_bytes()); - source - }); + // Write to stdin in a new thread, so that we can read from stdout on this + // thread. This keeps the child from blocking on writing to its stdout which + // might block us from writing to its stdin. + let stdin_handle = ::std::thread::spawn(move || { + let _ = child_stdin.write_all(source.as_bytes()); + source + }); - let mut output = vec![]; - io::copy(&mut child_stdout, &mut output)?; + let mut output = vec![]; + io::copy(&mut child_stdout, &mut output)?; - let status = child.wait()?; - let source = stdin_handle.join().expect( - "The thread writing to rustfmt's stdin doesn't do \ - anything that could panic", - ); + let status = child.wait()?; + let source = stdin_handle.join().expect( + "The thread writing to rustfmt's stdin doesn't do \ + anything that could panic", + ); - match String::from_utf8(output) { - Ok(bindings) => match status.code() { - Some(0) => Ok(bindings), - Some(2) => Err(io::Error::new( - io::ErrorKind::Other, - "Rustfmt parsing errors.".to_string(), - )), - Some(3) => { - rustfmt_non_fatal_error_diagnostic( - "Rustfmt could not format some lines", - &self.options, - ); - Ok(bindings) - } - _ => Err(io::Error::new( - io::ErrorKind::Other, - "Internal rustfmt error".to_string(), - )), - }, - _ => Ok(source), - } + match String::from_utf8(output) { + Ok(bindings) => match status.code() { + Some(0) => Ok(bindings), + Some(2) => Err(io::Error::new( + io::ErrorKind::Other, + "Rustfmt parsing errors.".to_string(), + )), + Some(3) => { + rustfmt_non_fatal_error_diagnostic( + "Rustfmt could not format some lines", + &options, + ); + Ok(bindings) + } + _ => Err(io::Error::new( + io::ErrorKind::Other, + "Internal rustfmt error".to_string(), + )), + }, + _ => Ok(source), } } diff --git a/bindgen/options/cli.rs b/bindgen/options/cli.rs index 287d781375..2fe300560f 100644 --- a/bindgen/options/cli.rs +++ b/bindgen/options/cli.rs @@ -1,10 +1,17 @@ #![allow(unused_qualifications)] // Clap somehow generates a lot of these use crate::{ - builder, callbacks::{ + builder, + callbacks::{ AttributeInfo, AttributeItemKind, DeriveInfo, ItemInfo, ParseCallbacks, TypeKind, - }, features::{RustEdition, EARLIEST_STABLE_RUST}, ir::function::FunctionKind, regex_set::RegexSet, Abi, AliasVariation, Builder, CodegenConfig, EnumVariation, FieldVisibilityKind, Formatter, HashSet, MacroTypeVariation, NonCopyUnionStyle, RustTarget + }, + features::{RustEdition, EARLIEST_STABLE_RUST}, + ir::function::FunctionKind, + regex_set::RegexSet, + Abi, AliasVariation, Builder, CodegenConfig, EnumVariation, + FieldVisibilityKind, Formatter, HashSet, MacroTypeVariation, + NonCopyUnionStyle, RustTarget, }; use clap::{ error::{Error, ErrorKind}, @@ -765,13 +772,11 @@ where Some(AttributeItemKind::Union) => { "--with-attribute-custom-union" } - Some(AttributeItemKind::Var) => { - "--with-attribute-custom-var" - } + Some(AttributeItemKind::Var) => "--with-attribute-custom-var", Some(AttributeItemKind::Function(_)) => { "--with-attribute-custom-function" } - }; + }; let attributes = self.attributes.join(","); @@ -785,7 +790,11 @@ where args } - fn process_attributes(&self, info: &AttributeInfo<'_>, attrs: &mut HashSet) { + fn process_attributes( + &self, + info: &AttributeInfo<'_>, + attrs: &mut HashSet, + ) { if self.kind.map_or(true, |kind| kind == info.kind) && self.regex_set.matches(info.name) { From ab272ad04d40c5ac0b268da9cd03ce15674cd0f4 Mon Sep 17 00:00:00 2001 From: oberrich Date: Thu, 6 Feb 2025 09:53:10 +0100 Subject: [PATCH 11/66] feat(attrs): always use `prettyplease::unparse` and some refactoring --- bindgen/codegen/mod.rs | 140 ++++++++++++++++++++--------------------- 1 file changed, 68 insertions(+), 72 deletions(-) diff --git a/bindgen/codegen/mod.rs b/bindgen/codegen/mod.rs index 3e2257ffd4..ceb705a06e 100644 --- a/bindgen/codegen/mod.rs +++ b/bindgen/codegen/mod.rs @@ -232,17 +232,13 @@ where .collect() } -fn process_attributes( - result: &mut CodegenResult, - item: &Item, - ctx: &BindgenContext, - mut attrs: Vec, - kind: AttributeItemKind, -) -> Vec { - attrs.extend(parse_tokens(item.annotations().attributes())); - - let mut attrs_out = vec![]; - if !attrs.is_empty() && attrs.iter().any(|attr| !attr.is_empty()) { +// ["# [repr (C)]", "# [repr (align (4))]", "# [derive (Debug , Default , Copy , Clone)]"] +// ["#[repr(C)]", "#[repr(align(4))]", "#[derive(Debug, Default, Copy, Clone)]"] +fn format_attribute_tokens(attrs: Vec) -> Vec { + if attrs.is_empty() || !attrs.iter().any(|attr| !attr.is_empty()) { + vec![] + } else { + println!("cargo:warning=attrs in: {:?}", attrs.iter().map(|x|x.to_string()).collect_vec()); // If this ever errors, we may have to - depending on the attributes - use different dummy items to // attach the attributes to. This is necessary to get a valid unparse from prettyplease/rustfmt let attrs_with_body = quote! { @@ -250,14 +246,11 @@ fn process_attributes( fn body() {} }; + let mut attrs = vec![]; let mut comments = vec![]; let mut block_comment = false; - // TODO: It might be a good idea to just default to prettyplease::unparse here - // TODO: as the user may have `None` in their config and `Rustfmt` invocations can - // TODO: become quite expensive when ran against each attribute. - for line in format_tokens(ctx.options(), &attrs_with_body) - .expect("can format attrs") + for line in prettyplease::unparse(&syn::parse_quote!(#attrs_with_body)) .split('\n') .take_while(|line| !line.starts_with("fn body")) .join("\n") @@ -281,7 +274,7 @@ fn process_attributes( { comments.push(cleaned.to_string()); } else if trimmed.starts_with('#') { - attrs_out.push(line.into()); + attrs.push(line.into()); } if trimmed.ends_with("*/") { @@ -289,12 +282,24 @@ fn process_attributes( } } - if !comments.is_empty() { - attrs_out.push(format!("#[doc = \"{}\"]", comments.join("\n"))); + if !comments.is_empty() && comments.iter().any(|c| !c.is_empty()) { + attrs.push(format!("#[doc = \"{}\"]", comments.join("\n"))); } - //println!("cargo:warning=attrs: {attrs_out:?}"); + println!("cargo:warning=attrs: {attrs:?}"); + + attrs } +} + +fn process_attributes( + result: &mut CodegenResult, + item: &Item, + ctx: &BindgenContext, + mut attrs: Vec, + kind: AttributeItemKind, +) -> Vec { + let mut attrs = format_attribute_tokens(attrs); // TODO: Call add_attributes and renormalize attributes ctx.options().for_each_callback_mut(|cb| { @@ -303,16 +308,31 @@ fn process_attributes( name: &item.canonical_name(ctx), kind, }, - &mut attrs_out, + &mut attrs, ); }); // TODO: Store as token stream here result.set_attributes( item.id(), - attrs_out.iter().map(|t| t.to_string()).collect(), + attrs.iter().map(|t| t.to_string()).collect(), ); - parse_tokens(attrs_out) + parse_tokens(attrs) +} + +fn attrs_for_item(item: &Item, ctx: &BindgenContext) -> Vec { + let mut attrs = vec![]; + attrs.extend(parse_tokens(item.annotations().attributes())); + + if let Some(comment) = item.comment(ctx) { + attrs.push(attributes::doc(&comment)); + } + + if item.must_use(ctx) { + attrs.push(attributes::must_use()); + } + + attrs } struct WrapAsVariadic { @@ -783,12 +803,7 @@ impl CodeGenerator for Var { return; } - let mut attrs = vec![]; - if let Some(comment) = item.comment(ctx) { - attrs.push(attributes::doc(&comment)); - } - - attrs.extend(parse_tokens(item.annotations().attributes())); + let mut attrs = attrs_for_item(item, ctx); let var_ty = self.ty(); let ty = var_ty.to_rust_ty_or_opaque(ctx, &()); @@ -1144,13 +1159,7 @@ impl CodeGenerator for Type { ctx.options().default_alias_style }; - let mut attrs = vec![]; - - attrs.extend(parse_tokens(item.annotations().attributes())); - - if let Some(comment) = item.comment(ctx) { - attrs.push(attributes::doc(&comment)); - } + let mut attrs = attrs_for_item(item, ctx); if let Some(inner_attrs) = result.get_attributes(inner_item.id()) @@ -1210,6 +1219,13 @@ impl CodeGenerator for Type { pub type #rust_name }, AliasVariation::NewType | AliasVariation::NewTypeDeref => { + // NB: Attributes for the actual item as well as its `process_attribute` callback + // have already been processed. It may be a good idea to emit these attributes + // earlier, to have them included in the callback. + // + // All of these attributes are additional and quoted below the processed attributes + // TODO: Finally introduce a TypeAlias variant to at least forward the alias_style, + // TODO: it should also include from and to type (cannonical name) let mut attrs = vec![attributes::repr("transparent")]; let packed = false; // Types can't be packed in Rust. let derivable_traits = @@ -1229,8 +1245,6 @@ impl CodeGenerator for Type { .extend(custom_derives.iter().map(|s| s.as_str())); attrs.push(attributes::derives(&derives)); - // TODO: Add custom attributes from callback here - quote! { #( #attrs )* pub struct #rust_name @@ -1683,6 +1697,8 @@ impl FieldCodegen<'_> for FieldData { }; let mut field = quote! {}; + // NB: Item::comment(str) does this internally, fields aren't + // actually Items so we do this manually here. if ctx.options().generate_comments { if let Some(raw_comment) = self.comment() { let comment = ctx.options().process_comment(raw_comment); @@ -2554,16 +2570,14 @@ impl CodeGenerator for CompInfo { (quote! {}, quote! {}, quote! {}) }; - let mut attrs = vec![]; + + let mut attrs = attrs_for_item(item, ctx); let mut needs_clone_impl = false; let mut needs_default_impl = false; let mut needs_debug_impl = false; let mut needs_partialeq_impl = false; let needs_flexarray_impl = flex_array_generic.is_some(); - if let Some(comment) = item.comment(ctx) { - attrs.push(attributes::doc(&comment)); - } - + // if a type has both a "packed" attribute and an "align(N)" attribute, then check if the // "packed" attr is redundant, and do not include it if so. if packed && @@ -3551,6 +3565,8 @@ impl<'a> EnumBuilder<'a> { }; let mut doc = quote! {}; + // NB: Item::comment(str) does this internally, fields aren't + // actually Items so we do this manually here. if ctx.options().generate_comments { if let Some(raw_comment) = variant.comment() { let comment = ctx.options().process_comment(raw_comment); @@ -3825,7 +3841,7 @@ impl CodeGenerator for Enum { } }; - let mut attrs = vec![]; + let mut attrs = attrs_for_item(item, ctx); // TODO(emilio): Delegate this to the builders? match variation { @@ -3850,14 +3866,6 @@ impl CodeGenerator for Enum { _ => {} }; - if let Some(comment) = item.comment(ctx) { - attrs.push(attributes::doc(&comment)); - } - - if item.must_use(ctx) { - attrs.push(attributes::must_use()); - } - if !variation.is_const() { let packed = false; // Enums can't be packed in Rust. let mut derives = derives_of_item(item, ctx, packed); @@ -3932,10 +3940,6 @@ impl CodeGenerator for Enum { let repr = repr.to_rust_ty_or_opaque(ctx, item); let has_typedef = ctx.is_enum_typedef_combo(item.id()); - // The custom attribute callback may return a list of attributes; - // add them to the end of the list. - attrs.extend(parse_tokens(item.annotations().attributes().clone())); - let attrs = process_attributes( result, item, @@ -4772,26 +4776,18 @@ impl CodeGenerator for Function { result.saw_function(seen_symbol_name); } - let mut attrs = vec![]; - attrs.extend(parse_tokens(item.annotations().attributes())); - - let must_use = signature.must_use() || { - let ret_ty = signature - .return_type() - .into_resolver() - .through_type_refs() - .resolve(ctx); - ret_ty.must_use(ctx) - }; + let mut attrs = attrs_for_item(item, ctx); - if must_use { + // Resolve #[must_use] attribute through return type + if signature + .return_type() + .into_resolver() + .through_type_refs() + .resolve(ctx) + .must_use(ctx) { attrs.push(attributes::must_use()); } - if let Some(comment) = item.comment(ctx) { - attrs.push(attributes::doc(&comment)); - } - let abi = match signature.abi(ctx, Some(name)) { Err(err) => { if matches!(err, Error::UnsupportedAbi(_)) { From 6ec8523783fdcfc16bae4b2778285dccf52969fa Mon Sep 17 00:00:00 2001 From: oberrich Date: Thu, 6 Feb 2025 10:16:17 +0100 Subject: [PATCH 12/66] refactor: unnest codegen for var to reduce code duplication --- bindgen/codegen/mod.rs | 248 +++++++++++++++++++++-------------------- 1 file changed, 127 insertions(+), 121 deletions(-) diff --git a/bindgen/codegen/mod.rs b/bindgen/codegen/mod.rs index ceb705a06e..a448152d65 100644 --- a/bindgen/codegen/mod.rs +++ b/bindgen/codegen/mod.rs @@ -72,6 +72,7 @@ use std::cell::Cell; use std::collections::VecDeque; use std::ffi::CStr; use std::fmt::{self, Write}; +use std::mem::MaybeUninit; use std::ops::{Deref, DerefMut}; use std::str::{self, FromStr}; @@ -238,7 +239,10 @@ fn format_attribute_tokens(attrs: Vec) -> Vec { if attrs.is_empty() || !attrs.iter().any(|attr| !attr.is_empty()) { vec![] } else { - println!("cargo:warning=attrs in: {:?}", attrs.iter().map(|x|x.to_string()).collect_vec()); + println!( + "cargo:warning=attrs in: {:?}", + attrs.iter().map(|x| x.to_string()).collect_vec() + ); // If this ever errors, we may have to - depending on the attributes - use different dummy items to // attach the attributes to. This is necessary to get a valid unparse from prettyplease/rustfmt let attrs_with_body = quote! { @@ -285,9 +289,9 @@ fn format_attribute_tokens(attrs: Vec) -> Vec { if !comments.is_empty() && comments.iter().any(|c| !c.is_empty()) { attrs.push(format!("#[doc = \"{}\"]", comments.join("\n"))); } - + println!("cargo:warning=attrs: {attrs:?}"); - + attrs } } @@ -327,7 +331,7 @@ fn attrs_for_item(item: &Item, ctx: &BindgenContext) -> Vec { if let Some(comment) = item.comment(ctx) { attrs.push(attributes::doc(&comment)); } - + if item.must_use(ctx) { attrs.push(attributes::must_use()); } @@ -808,112 +812,11 @@ impl CodeGenerator for Var { let var_ty = self.ty(); let ty = var_ty.to_rust_ty_or_opaque(ctx, &()); - if let Some(val) = self.val() { - // TODO: Review why this is inside the if block - let attrs = process_attributes( - result, - item, - ctx, - attrs, - AttributeItemKind::Var, - ); - - match *val { - VarType::Bool(val) => { - result.push(quote! { - #(#attrs)* - pub const #canonical_ident : #ty = #val ; - }); - } - VarType::Int(val) => { - let int_kind = var_ty - .into_resolver() - .through_type_aliases() - .through_type_refs() - .resolve(ctx) - .expect_type() - .as_integer() - .unwrap(); - let val = if int_kind.is_signed() { - helpers::ast_ty::int_expr(val) - } else { - helpers::ast_ty::uint_expr(val as _) - }; - result.push(quote! { - #(#attrs)* - pub const #canonical_ident : #ty = #val ; - }); - } - VarType::String(ref bytes) => { - let prefix = ctx.trait_prefix(); - - let options = ctx.options(); - let rust_features = options.rust_features; - - let mut cstr_bytes = bytes.clone(); - cstr_bytes.push(0); - let len = proc_macro2::Literal::usize_unsuffixed( - cstr_bytes.len(), - ); - let cstr = - if options.generate_cstr && rust_features.const_cstr { - CStr::from_bytes_with_nul(&cstr_bytes).ok() - } else { - None - }; - - if let Some(cstr) = cstr { - let cstr_ty = quote! { ::#prefix::ffi::CStr }; - if rust_features.literal_cstr { - let cstr = proc_macro2::Literal::c_string(cstr); - result.push(quote! { - #(#attrs)* - pub const #canonical_ident: &#cstr_ty = #cstr; - }); - } else { - let bytes = - proc_macro2::Literal::byte_string(&cstr_bytes); - result.push(quote! { - #(#attrs)* - #[allow(unsafe_code)] - pub const #canonical_ident: &#cstr_ty = unsafe { - #cstr_ty::from_bytes_with_nul_unchecked(#bytes) - }; - }); - } - } else { - // TODO: Here we ignore the type we just made up, probably - // we should refactor how the variable type and ty ID work. - let array_ty = quote! { [u8; #len] }; - let bytes = - proc_macro2::Literal::byte_string(&cstr_bytes); - let lifetime = - if true { None } else { Some(quote! { 'static }) } - .into_iter(); + let is_extern_var = self.val().is_none(); + let mut link_name = MaybeUninit::<&str>::uninit(); - result.push(quote! { - #(#attrs)* - pub const #canonical_ident: &#(#lifetime )*#array_ty = #bytes ; - }); - } - } - VarType::Float(f) => { - if let Ok(expr) = helpers::ast_ty::float_expr(ctx, f) { - result.push(quote! { - #(#attrs)* - pub const #canonical_ident : #ty = #expr ; - }); - } - } - VarType::Char(c) => { - result.push(quote! { - #(#attrs)* - pub const #canonical_ident : #ty = #c ; - }); - } - } - } else { - let symbol: &str = self.link_name().unwrap_or_else(|| { + if is_extern_var { + link_name.write(self.link_name().unwrap_or_else(|| { let link_name = self.mangled_name().unwrap_or_else(|| self.name()); if utils::names_will_be_identical_after_mangling( @@ -926,16 +829,20 @@ impl CodeGenerator for Var { attrs.push(attributes::link_name::(link_name)); link_name } - }); + })); + } - let attrs = process_attributes( - result, - item, - ctx, - attrs, - AttributeItemKind::Var, - ); + let attrs = process_attributes( + result, + item, + ctx, + attrs, + AttributeItemKind::Var, + ); + if is_extern_var { + // SAFETY: We've already initialized `symbol` when dealing with extern variables. + let symbol: &str = unsafe { link_name.assume_init() }; let maybe_mut = if self.is_const() { quote! {} } else { @@ -969,6 +876,105 @@ impl CodeGenerator for Var { } else { result.push(tokens); } + + // Required for SAFETY of the match below + return; + } + + // SAFETY: This part only runs if `is_extern_var` is false, + // meaning `self.val()` must be `Some(_)` + // Note: `symbol` remains uninitialized here + match *unsafe { self.val().unwrap_unchecked() } { + VarType::Bool(val) => { + result.push(quote! { + #(#attrs)* + pub const #canonical_ident : #ty = #val ; + }); + } + VarType::Int(val) => { + let int_kind = var_ty + .into_resolver() + .through_type_aliases() + .through_type_refs() + .resolve(ctx) + .expect_type() + .as_integer() + .unwrap(); + let val = if int_kind.is_signed() { + helpers::ast_ty::int_expr(val) + } else { + helpers::ast_ty::uint_expr(val as _) + }; + result.push(quote! { + #(#attrs)* + pub const #canonical_ident : #ty = #val ; + }); + } + VarType::String(ref bytes) => { + let prefix = ctx.trait_prefix(); + + let options = ctx.options(); + let rust_features = options.rust_features; + + let mut cstr_bytes = bytes.clone(); + cstr_bytes.push(0); + let len = + proc_macro2::Literal::usize_unsuffixed(cstr_bytes.len()); + let cstr = if options.generate_cstr && rust_features.const_cstr + { + CStr::from_bytes_with_nul(&cstr_bytes).ok() + } else { + None + }; + + if let Some(cstr) = cstr { + let cstr_ty = quote! { ::#prefix::ffi::CStr }; + if rust_features.literal_cstr { + let cstr = proc_macro2::Literal::c_string(cstr); + result.push(quote! { + #(#attrs)* + pub const #canonical_ident: &#cstr_ty = #cstr; + }); + } else { + let bytes = + proc_macro2::Literal::byte_string(&cstr_bytes); + result.push(quote! { + #(#attrs)* + #[allow(unsafe_code)] + pub const #canonical_ident: &#cstr_ty = unsafe { + #cstr_ty::from_bytes_with_nul_unchecked(#bytes) + }; + }); + } + } else { + // TODO: Here we ignore the type we just made up, probably + // we should refactor how the variable type and ty ID work. + let array_ty = quote! { [u8; #len] }; + let bytes = proc_macro2::Literal::byte_string(&cstr_bytes); + let lifetime = + if true { None } else { Some(quote! { 'static }) } + .into_iter(); + + result.push(quote! { + #(#attrs)* + pub const #canonical_ident: &#(#lifetime )*#array_ty = #bytes ; + }); + } + } + VarType::Float(f) => { + if let Ok(expr) = helpers::ast_ty::float_expr(ctx, f) { + result.push(quote! { + #(#attrs)* + pub const #canonical_ident : #ty = #expr ; + }); + } + } + VarType::Char(c) => { + result.push(quote! { + #(#attrs)* + pub const #canonical_ident : #ty = #c ; + }); + } } } } @@ -1225,7 +1231,7 @@ impl CodeGenerator for Type { // // All of these attributes are additional and quoted below the processed attributes // TODO: Finally introduce a TypeAlias variant to at least forward the alias_style, - // TODO: it should also include from and to type (cannonical name) + // TODO: it should also include from and to type (cannonical name) let mut attrs = vec![attributes::repr("transparent")]; let packed = false; // Types can't be packed in Rust. let derivable_traits = @@ -2570,14 +2576,13 @@ impl CodeGenerator for CompInfo { (quote! {}, quote! {}, quote! {}) }; - let mut attrs = attrs_for_item(item, ctx); let mut needs_clone_impl = false; let mut needs_default_impl = false; let mut needs_debug_impl = false; let mut needs_partialeq_impl = false; let needs_flexarray_impl = flex_array_generic.is_some(); - + // if a type has both a "packed" attribute and an "align(N)" attribute, then check if the // "packed" attr is redundant, and do not include it if so. if packed && @@ -4784,7 +4789,8 @@ impl CodeGenerator for Function { .into_resolver() .through_type_refs() .resolve(ctx) - .must_use(ctx) { + .must_use(ctx) + { attrs.push(attributes::must_use()); } From 3e98b56ab639c2ce005dba4da748dd1ed3dc86b7 Mon Sep 17 00:00:00 2001 From: oberrich Date: Thu, 6 Feb 2025 10:18:06 +0100 Subject: [PATCH 13/66] style: spelling --- bindgen/codegen/mod.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/bindgen/codegen/mod.rs b/bindgen/codegen/mod.rs index a448152d65..ab580eb0b1 100644 --- a/bindgen/codegen/mod.rs +++ b/bindgen/codegen/mod.rs @@ -813,10 +813,10 @@ impl CodeGenerator for Var { let ty = var_ty.to_rust_ty_or_opaque(ctx, &()); let is_extern_var = self.val().is_none(); - let mut link_name = MaybeUninit::<&str>::uninit(); + let mut maybe_link_name = MaybeUninit::<&str>::uninit(); if is_extern_var { - link_name.write(self.link_name().unwrap_or_else(|| { + maybe_link_name.write(self.link_name().unwrap_or_else(|| { let link_name = self.mangled_name().unwrap_or_else(|| self.name()); if utils::names_will_be_identical_after_mangling( @@ -842,7 +842,7 @@ impl CodeGenerator for Var { if is_extern_var { // SAFETY: We've already initialized `symbol` when dealing with extern variables. - let symbol: &str = unsafe { link_name.assume_init() }; + let link_name: &str = unsafe { maybe_link_name.assume_init() }; let maybe_mut = if self.is_const() { quote! {} } else { @@ -865,7 +865,7 @@ impl CodeGenerator for Var { if ctx.options().dynamic_library_name.is_some() { result.dynamic_items().push_var( &canonical_ident, - symbol, + link_name, &self .ty() .to_rust_ty_or_opaque(ctx, &()) @@ -877,13 +877,13 @@ impl CodeGenerator for Var { result.push(tokens); } - // Required for SAFETY of the match below + // Required for SAFETY of the match below. return; } // SAFETY: This part only runs if `is_extern_var` is false, - // meaning `self.val()` must be `Some(_)` - // Note: `symbol` remains uninitialized here + // meaning `self.val()` must be `Some(_)`. + // Note: `symbol` remains uninitialized here. match *unsafe { self.val().unwrap_unchecked() } { VarType::Bool(val) => { result.push(quote! { From fe77c8bca3136a1ae7a87ed32694d67a19bfdf71 Mon Sep 17 00:00:00 2001 From: oberrich Date: Thu, 6 Feb 2025 10:20:04 +0100 Subject: [PATCH 14/66] docs: fix comments --- bindgen/codegen/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bindgen/codegen/mod.rs b/bindgen/codegen/mod.rs index ab580eb0b1..9dc73893e7 100644 --- a/bindgen/codegen/mod.rs +++ b/bindgen/codegen/mod.rs @@ -841,7 +841,7 @@ impl CodeGenerator for Var { ); if is_extern_var { - // SAFETY: We've already initialized `symbol` when dealing with extern variables. + // SAFETY: We've already initialized `maybe_link_name` when dealing with extern variables. let link_name: &str = unsafe { maybe_link_name.assume_init() }; let maybe_mut = if self.is_const() { quote! {} @@ -883,7 +883,7 @@ impl CodeGenerator for Var { // SAFETY: This part only runs if `is_extern_var` is false, // meaning `self.val()` must be `Some(_)`. - // Note: `symbol` remains uninitialized here. + // Note: `maybe_link_name` remains uninitialized here. match *unsafe { self.val().unwrap_unchecked() } { VarType::Bool(val) => { result.push(quote! { From 678e52b11cdce4245ae11375386dc9e9551a727d Mon Sep 17 00:00:00 2001 From: oberrich Date: Thu, 6 Feb 2025 10:32:26 +0100 Subject: [PATCH 15/66] fix: remove debug prints --- bindgen/codegen/mod.rs | 26 ++++++++------------------ 1 file changed, 8 insertions(+), 18 deletions(-) diff --git a/bindgen/codegen/mod.rs b/bindgen/codegen/mod.rs index 9dc73893e7..5d850fe62a 100644 --- a/bindgen/codegen/mod.rs +++ b/bindgen/codegen/mod.rs @@ -239,10 +239,6 @@ fn format_attribute_tokens(attrs: Vec) -> Vec { if attrs.is_empty() || !attrs.iter().any(|attr| !attr.is_empty()) { vec![] } else { - println!( - "cargo:warning=attrs in: {:?}", - attrs.iter().map(|x| x.to_string()).collect_vec() - ); // If this ever errors, we may have to - depending on the attributes - use different dummy items to // attach the attributes to. This is necessary to get a valid unparse from prettyplease/rustfmt let attrs_with_body = quote! { @@ -290,8 +286,6 @@ fn format_attribute_tokens(attrs: Vec) -> Vec { attrs.push(format!("#[doc = \"{}\"]", comments.join("\n"))); } - println!("cargo:warning=attrs: {attrs:?}"); - attrs } } @@ -300,7 +294,7 @@ fn process_attributes( result: &mut CodegenResult, item: &Item, ctx: &BindgenContext, - mut attrs: Vec, + attrs: Vec, kind: AttributeItemKind, ) -> Vec { let mut attrs = format_attribute_tokens(attrs); @@ -316,12 +310,9 @@ fn process_attributes( ); }); - // TODO: Store as token stream here - result.set_attributes( - item.id(), - attrs.iter().map(|t| t.to_string()).collect(), - ); - parse_tokens(attrs) + let attrs = parse_tokens(attrs); + result.set_attributes(item.id(), attrs.clone()); + attrs } fn attrs_for_item(item: &Item, ctx: &BindgenContext) -> Vec { @@ -397,7 +388,7 @@ struct CodegenResult<'a> { items_to_serialize: Vec<(ItemId, Option)>, /// Tracks attributes of items during codegen - item_attributes: HashMap>, + item_attributes: HashMap>, } impl<'a> CodegenResult<'a> { @@ -420,14 +411,14 @@ impl<'a> CodegenResult<'a> { } } - fn set_attributes(&mut self, item_id: ItemId, attributes: Vec) { + fn set_attributes(&mut self, item_id: ItemId, attributes: Vec) { *self .item_attributes .entry(item_id) .or_insert_with(Default::default) = attributes; } - fn get_attributes(&self, item_id: ItemId) -> Option<&Vec> { + fn get_attributes(&self, item_id: ItemId) -> Option<&Vec> { self.item_attributes.get(&item_id) } @@ -1171,8 +1162,7 @@ impl CodeGenerator for Type { result.get_attributes(inner_item.id()) { // Only apply attributes through type aliases when they are relevant to compilation - attrs.extend( - parse_tokens(inner_attrs) + attrs.extend(inner_attrs .into_iter() .filter(|t| !t.is_empty()) .map(|t| parse_quote! {#t}) From 724fc191482fae2ceffbbcafc8955a4bbde89eea Mon Sep 17 00:00:00 2001 From: oberrich Date: Thu, 6 Feb 2025 10:52:21 +0100 Subject: [PATCH 16/66] fix(attrs): use `Vec<_>` over `HashSet<_>` --- bindgen/codegen/mod.rs | 2 +- bindgen/codegen/postprocessing/merge_cfg_attributes.rs | 9 ++++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/bindgen/codegen/mod.rs b/bindgen/codegen/mod.rs index 5d850fe62a..bb6d52b5f4 100644 --- a/bindgen/codegen/mod.rs +++ b/bindgen/codegen/mod.rs @@ -4772,7 +4772,7 @@ impl CodeGenerator for Function { } let mut attrs = attrs_for_item(item, ctx); - + // Resolve #[must_use] attribute through return type if signature .return_type() diff --git a/bindgen/codegen/postprocessing/merge_cfg_attributes.rs b/bindgen/codegen/postprocessing/merge_cfg_attributes.rs index 663be607e9..af07f3f15c 100644 --- a/bindgen/codegen/postprocessing/merge_cfg_attributes.rs +++ b/bindgen/codegen/postprocessing/merge_cfg_attributes.rs @@ -1,5 +1,4 @@ use crate::HashMap; -use crate::HashSet; use itertools::Itertools; use proc_macro2::Span; use quote::{quote, ToTokens}; @@ -36,9 +35,9 @@ impl SyntheticMod { #[derive(Default, Clone)] struct AttributeSet { - cfg_attrs: HashSet, - cc_attrs: HashSet, - other_attrs: HashSet, + cfg_attrs: Vec, + cc_attrs: Vec, + other_attrs: Vec, unsafety: Option, abi: Option, } @@ -61,7 +60,7 @@ impl AttributeSet { } else { &mut attribute_set.other_attrs }; - target_set.insert(attr.clone()); + target_set.push(attr.clone()); } attribute_set.unsafety = unsafety; attribute_set.abi = abi; From 91928beec1a29bec7dea28763791986b36b17195 Mon Sep 17 00:00:00 2001 From: oberrich Date: Thu, 6 Feb 2025 10:55:36 +0100 Subject: [PATCH 17/66] chore: fix rustfmt check --- bindgen-integration/src/lib.rs | 1 - bindgen/codegen/mod.rs | 11 ++++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/bindgen-integration/src/lib.rs b/bindgen-integration/src/lib.rs index 9786c16e8e..b13068255b 100755 --- a/bindgen-integration/src/lib.rs +++ b/bindgen-integration/src/lib.rs @@ -353,7 +353,6 @@ fn test_custom_fn_attribute() { ); } - #[test] fn test_custom_attributes() { // The `add_attributes` callback should have added `#[cfg_attr(test, derive(PartialOrd))])` diff --git a/bindgen/codegen/mod.rs b/bindgen/codegen/mod.rs index bb6d52b5f4..60e6f98f45 100644 --- a/bindgen/codegen/mod.rs +++ b/bindgen/codegen/mod.rs @@ -411,7 +411,11 @@ impl<'a> CodegenResult<'a> { } } - fn set_attributes(&mut self, item_id: ItemId, attributes: Vec) { + fn set_attributes( + &mut self, + item_id: ItemId, + attributes: Vec, + ) { *self .item_attributes .entry(item_id) @@ -1162,7 +1166,8 @@ impl CodeGenerator for Type { result.get_attributes(inner_item.id()) { // Only apply attributes through type aliases when they are relevant to compilation - attrs.extend(inner_attrs + attrs.extend( + inner_attrs .into_iter() .filter(|t| !t.is_empty()) .map(|t| parse_quote! {#t}) @@ -4772,7 +4777,7 @@ impl CodeGenerator for Function { } let mut attrs = attrs_for_item(item, ctx); - + // Resolve #[must_use] attribute through return type if signature .return_type() From 3600115a5ab052763090776ba182e4196c92de56 Mon Sep 17 00:00:00 2001 From: oberrich Date: Thu, 6 Feb 2025 11:00:48 +0100 Subject: [PATCH 18/66] fix: crate version --- bindgen/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bindgen/Cargo.toml b/bindgen/Cargo.toml index 6cbc084063..c01f8f0c44 100644 --- a/bindgen/Cargo.toml +++ b/bindgen/Cargo.toml @@ -16,7 +16,7 @@ readme = "../README.md" repository = "https://github.com/rust-lang/rust-bindgen" documentation = "https://docs.rs/bindgen" homepage = "https://rust-lang.github.io/rust-bindgen/" -version = "0.69.5" +version = "0.71.1" build = "build.rs" rust-version.workspace = true edition.workspace = true From 31e3c5f2cf1262aa15efd926c2853166a5dda44c Mon Sep 17 00:00:00 2001 From: oberrich Date: Thu, 6 Feb 2025 11:05:10 +0100 Subject: [PATCH 19/66] fix: remove unused imports --- bindgen/codegen/mod.rs | 12 +++--------- bindgen/ir/comment.rs | 2 +- bindgen/lib.rs | 2 +- 3 files changed, 5 insertions(+), 11 deletions(-) diff --git a/bindgen/codegen/mod.rs b/bindgen/codegen/mod.rs index 60e6f98f45..b37b6af4b7 100644 --- a/bindgen/codegen/mod.rs +++ b/bindgen/codegen/mod.rs @@ -25,7 +25,6 @@ use crate::callbacks::{ DiscoveredItemId, FieldInfo, TypeKind as DeriveTypeKind, }; use crate::codegen::error::Error; -use crate::comment::{self, preprocess}; use crate::ir::analysis::{HasVtable, Sizedness}; use crate::ir::annotations::{ Annotations, FieldAccessorKind, FieldVisibilityKind, @@ -58,15 +57,10 @@ use crate::ir::var::Var; use crate::quote::{ToTokens, TokenStreamExt}; use itertools::Itertools; -use proc_macro2::{Group, Ident, Span, TokenStream, TokenTree}; -use syn::meta::ParseNestedMeta; -use syn::parse::{ParseBuffer, ParseStream}; -use syn::{ - parse, parse_quote, Attribute, Expr, ForeignItemMacro, Lit, Meta, MetaList, - MetaNameValue, -}; +use proc_macro2::{Ident, Span, TokenStream}; +use syn::{parse_quote, Attribute}; -use crate::{format_tokens, Entry, HashMap, HashSet}; +use crate::{Entry, HashMap, HashSet}; use std::borrow::Cow; use std::cell::Cell; use std::collections::VecDeque; diff --git a/bindgen/ir/comment.rs b/bindgen/ir/comment.rs index 49828dd4e3..9d94ac2e6b 100644 --- a/bindgen/ir/comment.rs +++ b/bindgen/ir/comment.rs @@ -21,7 +21,7 @@ pub(crate) fn preprocess(comment: &str) -> String { } /// Gets the kind of the doc comment, if it is one. -pub(crate) fn kind(comment: &str) -> Option { +fn kind(comment: &str) -> Option { if comment.starts_with("/*") { Some(Kind::MultiLine) } else if comment.starts_with("//") { diff --git a/bindgen/lib.rs b/bindgen/lib.rs index 2db7a0a6fe..f9200a43dd 100644 --- a/bindgen/lib.rs +++ b/bindgen/lib.rs @@ -977,7 +977,7 @@ pub(crate) fn rustfmt_path( } /// Formats a token stream with the formatter set up in `BindgenOptions`. -pub(crate) fn format_tokens( +fn format_tokens( options: &BindgenOptions, tokens: &proc_macro2::TokenStream, ) -> io::Result { From 3c552debb20636bd4783806da4c8f341871f6b18 Mon Sep 17 00:00:00 2001 From: oberrich Date: Thu, 6 Feb 2025 11:08:47 +0100 Subject: [PATCH 20/66] fix: cli build --- bindgen/codegen/mod.rs | 3 ++- bindgen/options/cli.rs | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/bindgen/codegen/mod.rs b/bindgen/codegen/mod.rs index b37b6af4b7..b6f4dc77cd 100644 --- a/bindgen/codegen/mod.rs +++ b/bindgen/codegen/mod.rs @@ -59,6 +59,7 @@ use crate::quote::{ToTokens, TokenStreamExt}; use itertools::Itertools; use proc_macro2::{Ident, Span, TokenStream}; use syn::{parse_quote, Attribute}; +use prettyplease::unparse; use crate::{Entry, HashMap, HashSet}; use std::borrow::Cow; @@ -244,7 +245,7 @@ fn format_attribute_tokens(attrs: Vec) -> Vec { let mut comments = vec![]; let mut block_comment = false; - for line in prettyplease::unparse(&syn::parse_quote!(#attrs_with_body)) + for line in unparse(&syn::parse_quote!(#attrs_with_body)) .split('\n') .take_while(|line| !line.starts_with("fn body")) .join("\n") diff --git a/bindgen/options/cli.rs b/bindgen/options/cli.rs index 2fe300560f..fb2ea14c04 100644 --- a/bindgen/options/cli.rs +++ b/bindgen/options/cli.rs @@ -10,7 +10,7 @@ use crate::{ ir::function::FunctionKind, regex_set::RegexSet, Abi, AliasVariation, Builder, CodegenConfig, EnumVariation, - FieldVisibilityKind, Formatter, HashSet, MacroTypeVariation, + FieldVisibilityKind, Formatter, MacroTypeVariation, NonCopyUnionStyle, RustTarget, }; use clap::{ @@ -793,7 +793,7 @@ where fn process_attributes( &self, info: &AttributeInfo<'_>, - attrs: &mut HashSet, + attrs: &mut Vec, ) { if self.kind.map_or(true, |kind| kind == info.kind) && self.regex_set.matches(info.name) @@ -1045,7 +1045,7 @@ where "--with-attribute-custom-union", ), ( - with_attribute_custom_function, + with_attribute_custom_var, Some(AttributeItemKind::Var), "--with-attribute-custom-var", ), From 8dc496331834826b1ca2c77f33d79869a22a60c5 Mon Sep 17 00:00:00 2001 From: oberrich Date: Thu, 6 Feb 2025 11:11:32 +0100 Subject: [PATCH 21/66] chore: run rustfmt on `codegen/mod.rs` --- bindgen/codegen/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bindgen/codegen/mod.rs b/bindgen/codegen/mod.rs index b6f4dc77cd..5c7a77b7c5 100644 --- a/bindgen/codegen/mod.rs +++ b/bindgen/codegen/mod.rs @@ -57,9 +57,9 @@ use crate::ir::var::Var; use crate::quote::{ToTokens, TokenStreamExt}; use itertools::Itertools; +use prettyplease::unparse; use proc_macro2::{Ident, Span, TokenStream}; use syn::{parse_quote, Attribute}; -use prettyplease::unparse; use crate::{Entry, HashMap, HashSet}; use std::borrow::Cow; From b2b31814771fc0eb3542165527394063e23ef2c4 Mon Sep 17 00:00:00 2001 From: oberrich Date: Thu, 6 Feb 2025 11:14:37 +0100 Subject: [PATCH 22/66] chore: run rustfmt on `options/cli.rs` --- bindgen/options/cli.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bindgen/options/cli.rs b/bindgen/options/cli.rs index fb2ea14c04..f1e1a77c72 100644 --- a/bindgen/options/cli.rs +++ b/bindgen/options/cli.rs @@ -10,8 +10,8 @@ use crate::{ ir::function::FunctionKind, regex_set::RegexSet, Abi, AliasVariation, Builder, CodegenConfig, EnumVariation, - FieldVisibilityKind, Formatter, MacroTypeVariation, - NonCopyUnionStyle, RustTarget, + FieldVisibilityKind, Formatter, MacroTypeVariation, NonCopyUnionStyle, + RustTarget, }; use clap::{ error::{Error, ErrorKind}, From 1be9954883e7e1231fb834aab185fae6fe2212ab Mon Sep 17 00:00:00 2001 From: oberrich Date: Thu, 6 Feb 2025 11:26:33 +0100 Subject: [PATCH 23/66] fix: make clippy happy --- bindgen/codegen/mod.rs | 28 +++++++++---------- .../postprocessing/merge_cfg_attributes.rs | 4 +-- bindgen/codegen/serialize.rs | 4 +-- bindgen/ir/analysis/derive.rs | 4 +-- bindgen/ir/comment.rs | 2 +- bindgen/ir/comp.rs | 2 +- bindgen/ir/context.rs | 4 +-- bindgen/ir/dot.rs | 2 +- bindgen/lib.rs | 10 +++---- 9 files changed, 30 insertions(+), 30 deletions(-) diff --git a/bindgen/codegen/mod.rs b/bindgen/codegen/mod.rs index 5c7a77b7c5..79c6b7bd5a 100644 --- a/bindgen/codegen/mod.rs +++ b/bindgen/codegen/mod.rs @@ -230,7 +230,7 @@ where // ["# [repr (C)]", "# [repr (align (4))]", "# [derive (Debug , Default , Copy , Clone)]"] // ["#[repr(C)]", "#[repr(align(4))]", "#[derive(Debug, Default, Copy, Clone)]"] -fn format_attribute_tokens(attrs: Vec) -> Vec { +fn format_attribute_tokens(attrs: &[TokenStream]) -> Vec { if attrs.is_empty() || !attrs.iter().any(|attr| !attr.is_empty()) { vec![] } else { @@ -289,7 +289,7 @@ fn process_attributes( result: &mut CodegenResult, item: &Item, ctx: &BindgenContext, - attrs: Vec, + attrs: &[TokenStream], kind: AttributeItemKind, ) -> Vec { let mut attrs = format_attribute_tokens(attrs); @@ -414,7 +414,7 @@ impl<'a> CodegenResult<'a> { *self .item_attributes .entry(item_id) - .or_insert_with(Default::default) = attributes; + .or_default() = attributes; } fn get_attributes(&self, item_id: ItemId) -> Option<&Vec> { @@ -826,7 +826,7 @@ impl CodeGenerator for Var { result, item, ctx, - attrs, + &attrs, AttributeItemKind::Var, ); @@ -1163,7 +1163,7 @@ impl CodeGenerator for Type { // Only apply attributes through type aliases when they are relevant to compilation attrs.extend( inner_attrs - .into_iter() + .iter() .filter(|t| !t.is_empty()) .map(|t| parse_quote! {#t}) .filter_map(|attr: Attribute| { @@ -1182,7 +1182,7 @@ impl CodeGenerator for Type { result, item, ctx, - attrs, + &attrs, AttributeItemKind::Struct, ); @@ -2691,7 +2691,7 @@ impl CodeGenerator for CompInfo { result, item, ctx, - attrs, + &attrs, if is_rust_union { AttributeItemKind::Union } else { @@ -3290,7 +3290,7 @@ impl Method { exprs[0] = quote! { self }; - }; + } let call = quote! { #function_name (#( #exprs ),* ) @@ -3322,7 +3322,7 @@ impl Method { result, function_item, ctx, - attrs, + &attrs, AttributeItemKind::Function(FunctionKind::Method(self.kind())), ); @@ -3859,7 +3859,7 @@ impl CodeGenerator for Enum { } } _ => {} - }; + } if !variation.is_const() { let packed = false; // Enums can't be packed in Rust. @@ -3897,7 +3897,7 @@ impl CodeGenerator for Enum { result, item, ctx, - attrs, + &attrs, AttributeItemKind::Enum, ); } @@ -3939,7 +3939,7 @@ impl CodeGenerator for Enum { result, item, ctx, - attrs.clone(), + &attrs, AttributeItemKind::Enum, ); @@ -4820,7 +4820,7 @@ impl CodeGenerator for Function { mangled_name, Some(abi), )) - .then(|| mangled_name) + .then_some(mangled_name) }); if let Some(link_name) = link_name_attr { @@ -4892,7 +4892,7 @@ impl CodeGenerator for Function { result, item, ctx, - attrs, + &attrs, AttributeItemKind::Function(FunctionKind::Function), ); diff --git a/bindgen/codegen/postprocessing/merge_cfg_attributes.rs b/bindgen/codegen/postprocessing/merge_cfg_attributes.rs index af07f3f15c..cc60ce7f23 100644 --- a/bindgen/codegen/postprocessing/merge_cfg_attributes.rs +++ b/bindgen/codegen/postprocessing/merge_cfg_attributes.rs @@ -192,7 +192,7 @@ impl Visitor { Item::Impl(ItemImpl { ref mut attrs, .. }) | Item::Fn(ItemFn { ref mut attrs, .. }) => { let attr_set = AttributeSet::new(attrs, None, None); - *attrs = attr_set.other_attrs.iter().cloned().collect(); + attrs.clone_from(&attr_set.other_attrs); self.insert_item_into_mod(attr_set, item); } Item::ForeignMod(foreign_mod) => { @@ -227,7 +227,7 @@ impl Visitor { Some(foreign_mod.abi.clone()), ); attr_set.extend(inner_attrs, inner_unsafety, inner_abi); - *inner_attrs = attr_set.other_attrs.iter().cloned().collect(); + *inner_attrs = attr_set.other_attrs.clone(); self.insert_item_into_mod( attr_set, diff --git a/bindgen/codegen/serialize.rs b/bindgen/codegen/serialize.rs index c7bb6cecef..9af48aa8ff 100644 --- a/bindgen/codegen/serialize.rs +++ b/bindgen/codegen/serialize.rs @@ -368,7 +368,7 @@ impl<'a> CSerialize<'a> for Type { match comp_info.kind() { CompKind::Struct => write!(writer, "struct {name}")?, CompKind::Union => write!(writer, "union {name}")?, - }; + } } TypeKind::Enum(_enum_ty) => { if self.is_const() { @@ -384,7 +384,7 @@ impl<'a> CSerialize<'a> for Type { loc: get_loc(item), }) } - }; + } if !stack.is_empty() { write!(writer, " ")?; diff --git a/bindgen/ir/analysis/derive.rs b/bindgen/ir/analysis/derive.rs index 6c66998bee..eaa20fff46 100644 --- a/bindgen/ir/analysis/derive.rs +++ b/bindgen/ir/analysis/derive.rs @@ -197,7 +197,7 @@ impl CannotDerive<'_> { self.derive_trait ); } - }; + } return layout_can_derive; } @@ -355,7 +355,7 @@ impl CannotDerive<'_> { self.derive_trait ); } - }; + } return layout_can_derive; } } diff --git a/bindgen/ir/comment.rs b/bindgen/ir/comment.rs index 9d94ac2e6b..d556cf0e6f 100644 --- a/bindgen/ir/comment.rs +++ b/bindgen/ir/comment.rs @@ -44,7 +44,7 @@ fn preprocess_single_lines(comment: &str) -> String { lines.join("\n") } -pub(crate) fn preprocess_multi_line(comment: &str) -> String { +fn preprocess_multi_line(comment: &str) -> String { let comment = comment .trim_start_matches('/') .trim_end_matches('/') diff --git a/bindgen/ir/comp.rs b/bindgen/ir/comp.rs index c24b951b29..0179ef9687 100644 --- a/bindgen/ir/comp.rs +++ b/bindgen/ir/comp.rs @@ -1855,7 +1855,7 @@ impl IsOpaque for CompInfo { // See https://github.com/rust-lang/rust-bindgen/issues/537 and // https://github.com/rust-lang/rust/issues/33158 if self.is_packed(ctx, layout.as_ref()) && - layout.map_or(false, |l| l.align > 1) + layout.is_some_and(|l| l.align > 1) { warn!("Found a type that is both packed and aligned to greater than \ 1; Rust before version 1.33 doesn't have `#[repr(packed(N))]`, so we \ diff --git a/bindgen/ir/context.rs b/bindgen/ir/context.rs index c6bc9025ec..458298c61b 100644 --- a/bindgen/ir/context.rs +++ b/bindgen/ir/context.rs @@ -933,7 +933,7 @@ If you encounter an error missing from this list, please file an issue or a PR!" *ty.kind() { typerefs.push((id, *ty, loc, parent_id)); - }; + } } typerefs } @@ -3095,7 +3095,7 @@ impl TemplateParameters for PartialType { num_params += 1; } _ => {} - }; + } clang_sys::CXChildVisit_Continue }); num_params diff --git a/bindgen/ir/dot.rs b/bindgen/ir/dot.rs index 0ccee42c0d..1562057b0e 100644 --- a/bindgen/ir/dot.rs +++ b/bindgen/ir/dot.rs @@ -41,7 +41,7 @@ where if is_allowlisted { "black" } else { "gray" } )?; item.dot_attributes(ctx, &mut dot_file)?; - writeln!(&mut dot_file, r#" >];"#)?; + writeln!(&mut dot_file, r" >];")?; item.trace( ctx, diff --git a/bindgen/lib.rs b/bindgen/lib.rs index f9200a43dd..561a43df4b 100644 --- a/bindgen/lib.rs +++ b/bindgen/lib.rs @@ -563,7 +563,7 @@ impl BindgenOptions { self.parse_callbacks .iter() .filter_map(|cb| f(cb.as_ref())) - .last() + .next_back() } fn all_callbacks( @@ -776,7 +776,7 @@ impl Bindings { 0, format!("--target={effective_target}").into_boxed_str(), ); - }; + } fn detect_include_paths(options: &mut BindgenOptions) { if !options.detect_include_paths { @@ -993,7 +993,7 @@ fn format_tokens( Formatter::Rustfmt => (), } - let rustfmt = rustfmt_path(&options)?; + let rustfmt = rustfmt_path(options)?; let mut cmd = Command::new(&*rustfmt); cmd.stdin(Stdio::piped()).stdout(Stdio::piped()); @@ -1044,7 +1044,7 @@ fn format_tokens( Some(3) => { rustfmt_non_fatal_error_diagnostic( "Rustfmt could not format some lines", - &options, + options, ); Ok(bindings) } @@ -1189,7 +1189,7 @@ pub fn clang_version() -> ClangVersion { }; } } - }; + } ClangVersion { parsed: None, full: raw_v.clone(), From 3d1ff2c004478d3f97a7a6c51e976fa0a23e4914 Mon Sep 17 00:00:00 2001 From: oberrich Date: Thu, 6 Feb 2025 11:33:16 +0100 Subject: [PATCH 24/66] chore: run rustfmt on `codegen/mod.rs` --- bindgen/codegen/mod.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/bindgen/codegen/mod.rs b/bindgen/codegen/mod.rs index 79c6b7bd5a..5d80dae526 100644 --- a/bindgen/codegen/mod.rs +++ b/bindgen/codegen/mod.rs @@ -411,10 +411,7 @@ impl<'a> CodegenResult<'a> { item_id: ItemId, attributes: Vec, ) { - *self - .item_attributes - .entry(item_id) - .or_default() = attributes; + *self.item_attributes.entry(item_id).or_default() = attributes; } fn get_attributes(&self, item_id: ItemId) -> Option<&Vec> { From a833e10ff1de9c47d27eb7a1a8a00bceaf6dce82 Mon Sep 17 00:00:00 2001 From: oberrich Date: Thu, 6 Feb 2025 11:43:42 +0100 Subject: [PATCH 25/66] feat(attrs): always enable `prettyplease` --- Cargo.toml | 2 +- bindgen-cli/Cargo.toml | 3 +-- bindgen/Cargo.toml | 4 ++-- bindgen/lib.rs | 4 ---- 4 files changed, 4 insertions(+), 9 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 7ba27c1658..94e7d3b1a0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,7 +36,7 @@ libloading = "0.8" log = "0.4" objc = "0.2" owo-colors = "4.1.0" -prettyplease = "0.2.7" +prettyplease = { version = "0.2.7" } proc-macro2 = "1.0.80" quickcheck = "1.0" quote = { version = "1", default-features = false } diff --git a/bindgen-cli/Cargo.toml b/bindgen-cli/Cargo.toml index 52fcaaeb1b..628e800249 100644 --- a/bindgen-cli/Cargo.toml +++ b/bindgen-cli/Cargo.toml @@ -22,7 +22,7 @@ path = "main.rs" name = "bindgen" [dependencies] -bindgen = { workspace = true, features = ["__cli", "experimental", "prettyplease"] } +bindgen = { workspace = true, features = ["__cli", "experimental"] } env_logger = { workspace = true, optional = true } log = { workspace = true, optional = true } proc-macro2.workspace = true @@ -33,7 +33,6 @@ default = ["logging", "runtime"] logging = ["bindgen/logging", "dep:env_logger", "dep:log"] static = ["bindgen/static"] runtime = ["bindgen/runtime"] -prettyplease = ["bindgen/prettyplease"] ## The following features are for internal use and they shouldn't be used if ## you're not hacking on bindgen diff --git a/bindgen/Cargo.toml b/bindgen/Cargo.toml index c01f8f0c44..f3822c2b07 100644 --- a/bindgen/Cargo.toml +++ b/bindgen/Cargo.toml @@ -34,7 +34,7 @@ clap = { workspace = true, features = ["derive"], optional = true } clap_complete = { workspace = true, optional = true } itertools = { workspace = true } log = { workspace = true, optional = true } -prettyplease = { workspace = true, optional = true, features = ["verbatim"] } +prettyplease = { workspace = true, features = ["verbatim"] } proc-macro2.workspace = true quote.workspace = true regex = { workspace = true, features = ["std", "unicode-perl"] } @@ -43,7 +43,7 @@ shlex.workspace = true syn = { workspace = true, features = ["full", "extra-traits", "visit-mut"] } [features] -default = ["logging", "prettyplease", "runtime"] +default = ["logging", "runtime"] logging = ["dep:log"] static = ["clang-sys/static"] runtime = ["clang-sys/runtime"] diff --git a/bindgen/lib.rs b/bindgen/lib.rs index 561a43df4b..9687b08dff 100644 --- a/bindgen/lib.rs +++ b/bindgen/lib.rs @@ -174,7 +174,6 @@ pub enum Formatter { None, /// Use `rustfmt` to format the bindings. Rustfmt, - #[cfg(feature = "prettyplease")] /// Use `prettyplease` to format the bindings. Prettyplease, } @@ -192,7 +191,6 @@ impl FromStr for Formatter { match s { "none" => Ok(Self::None), "rustfmt" => Ok(Self::Rustfmt), - #[cfg(feature = "prettyplease")] "prettyplease" => Ok(Self::Prettyplease), _ => Err(format!("`{s}` is not a valid formatter")), } @@ -204,7 +202,6 @@ impl std::fmt::Display for Formatter { let s = match self { Self::None => "none", Self::Rustfmt => "rustfmt", - #[cfg(feature = "prettyplease")] Self::Prettyplease => "prettyplease", }; @@ -986,7 +983,6 @@ fn format_tokens( match options.formatter { Formatter::None => return Ok(tokens.to_string()), - #[cfg(feature = "prettyplease")] Formatter::Prettyplease => { return Ok(prettyplease::unparse(&syn::parse_quote!(#tokens))); } From b4eb4fc5e11a7212540710c131f46383dfa09439 Mon Sep 17 00:00:00 2001 From: oberrich Date: Thu, 6 Feb 2025 11:49:39 +0100 Subject: [PATCH 26/66] fix(attrs): emit comment as first attribute --- bindgen/codegen/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bindgen/codegen/mod.rs b/bindgen/codegen/mod.rs index 5d80dae526..3415125ef9 100644 --- a/bindgen/codegen/mod.rs +++ b/bindgen/codegen/mod.rs @@ -278,7 +278,7 @@ fn format_attribute_tokens(attrs: &[TokenStream]) -> Vec { } if !comments.is_empty() && comments.iter().any(|c| !c.is_empty()) { - attrs.push(format!("#[doc = \"{}\"]", comments.join("\n"))); + attrs.insert(0, format!("#[doc = \"{}\"]", comments.join("\n"))); } attrs From 786c1bc41714e1a550597cc319abba7d1749f737 Mon Sep 17 00:00:00 2001 From: oberrich Date: Thu, 6 Feb 2025 12:09:47 +0100 Subject: [PATCH 27/66] style: minor fixes --- Cargo.toml | 2 +- bindgen/callbacks.rs | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 94e7d3b1a0..7ba27c1658 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,7 +36,7 @@ libloading = "0.8" log = "0.4" objc = "0.2" owo-colors = "4.1.0" -prettyplease = { version = "0.2.7" } +prettyplease = "0.2.7" proc-macro2 = "1.0.80" quickcheck = "1.0" quote = { version = "1", default-features = false } diff --git a/bindgen/callbacks.rs b/bindgen/callbacks.rs index 834d8d5a46..0cbcadd1a5 100644 --- a/bindgen/callbacks.rs +++ b/bindgen/callbacks.rs @@ -241,7 +241,6 @@ pub struct DeriveInfo<'a> { pub kind: TypeKind, } -/// Relevant information about a type to which new attributes will be added using /// Relevant information about an item to which new attributes will be added using /// [`ParseCallbacks::add_attributes`]. #[derive(Debug)] From dc468eeaec5481e04d194948b70b9ffc2c3a826b Mon Sep 17 00:00:00 2001 From: oberrich Date: Thu, 6 Feb 2025 12:41:22 +0100 Subject: [PATCH 28/66] fix(attrs): only quote `cfg_attrs` for constified enum variant `impl`s --- bindgen/codegen/mod.rs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/bindgen/codegen/mod.rs b/bindgen/codegen/mod.rs index 3415125ef9..7c344c57a6 100644 --- a/bindgen/codegen/mod.rs +++ b/bindgen/codegen/mod.rs @@ -2680,10 +2680,6 @@ impl CodeGenerator for CompInfo { attrs.push(attributes::derives(&derives)); } - if item.must_use(ctx) { - attrs.push(attributes::must_use()); - } - let attrs = process_attributes( result, item, @@ -3940,6 +3936,15 @@ impl CodeGenerator for Enum { AttributeItemKind::Enum, ); + let cfg_attrs = attrs + .iter() + .filter(|t| !t.is_empty()) + .map(|t| parse_quote! {#t}) + .filter(|attr: &Attribute| { + attr.path().is_ident("cfg") || attr.path().is_ident("link") + }) + .collect_vec(); + let mut builder = EnumBuilder::new( &name, attrs.clone(), @@ -4011,7 +4016,7 @@ impl CodeGenerator for Enum { ctx.rust_ident_raw(&*mangled_name); // TODO: Only quote in #(#cfg_attrs)* result.push(quote! { - #(#attrs)* + #(#cfg_attrs)* impl #enum_rust_ty { pub const #variant_name : #enum_rust_ty = #enum_canonical_name :: #existing_variant_name ; From 7834cdc8256046017cdab4bc2846db4d7dc91460 Mon Sep 17 00:00:00 2001 From: oberrich Date: Thu, 6 Feb 2025 13:01:38 +0100 Subject: [PATCH 29/66] fix(regr): prepend space inside block comments --- bindgen/codegen/mod.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/bindgen/codegen/mod.rs b/bindgen/codegen/mod.rs index 7c344c57a6..a886be13b2 100644 --- a/bindgen/codegen/mod.rs +++ b/bindgen/codegen/mod.rs @@ -244,6 +244,7 @@ fn format_attribute_tokens(attrs: &[TokenStream]) -> Vec { let mut attrs = vec![]; let mut comments = vec![]; let mut block_comment = false; + let mut block_comment_count = 0; for line in unparse(&syn::parse_quote!(#attrs_with_body)) .split('\n') @@ -253,6 +254,7 @@ fn format_attribute_tokens(attrs: &[TokenStream]) -> Vec { { let trimmed = line.trim(); if trimmed.starts_with("/*") { + block_comment_count += 1; block_comment = true; } @@ -267,12 +269,13 @@ fn format_attribute_tokens(attrs: &[TokenStream]) -> Vec { trimmed.starts_with("///") || trimmed.starts_with("//") { - comments.push(cleaned.to_string()); + comments.push(format!("{}{}", if block_comment_count > 0 {" "} else {""}, cleaned.to_string())); } else if trimmed.starts_with('#') { attrs.push(line.into()); } if trimmed.ends_with("*/") { + block_comment_count = 0; block_comment = false; } } From d2b5b19cf39b8218b5826a83e0ed7f483413a516 Mon Sep 17 00:00:00 2001 From: oberrich Date: Thu, 6 Feb 2025 13:02:22 +0100 Subject: [PATCH 30/66] chore: run rustfmt on `codegen/mod.rs` --- bindgen/codegen/mod.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/bindgen/codegen/mod.rs b/bindgen/codegen/mod.rs index a886be13b2..f6616e7b42 100644 --- a/bindgen/codegen/mod.rs +++ b/bindgen/codegen/mod.rs @@ -269,7 +269,11 @@ fn format_attribute_tokens(attrs: &[TokenStream]) -> Vec { trimmed.starts_with("///") || trimmed.starts_with("//") { - comments.push(format!("{}{}", if block_comment_count > 0 {" "} else {""}, cleaned.to_string())); + comments.push(format!( + "{}{}", + if block_comment_count > 0 { " " } else { "" }, + cleaned.to_string() + )); } else if trimmed.starts_with('#') { attrs.push(line.into()); } From ed8fc84f5dcbf9a53a1170919afe268e06eab609 Mon Sep 17 00:00:00 2001 From: oberrich Date: Thu, 6 Feb 2025 13:04:36 +0100 Subject: [PATCH 31/66] chore: apply clippy fixes --- bindgen-tests/tests/tests.rs | 6 +++--- bindgen/codegen/mod.rs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/bindgen-tests/tests/tests.rs b/bindgen-tests/tests/tests.rs index b808dfcd43..3cc7cbe415 100644 --- a/bindgen-tests/tests/tests.rs +++ b/bindgen-tests/tests/tests.rs @@ -27,7 +27,7 @@ fn should_overwrite_expected() -> bool { return true; } assert!( - var == "0" || var == "", + var == "0" || var.is_empty(), "Invalid value of BINDGEN_OVERWRITE_EXPECTED" ); } @@ -57,7 +57,7 @@ fn error_diff_mismatch( if let Some(var) = env::var_os("BINDGEN_TESTS_DIFFTOOL") { //usecase: var = "meld" -> You can hand check differences let Some(std::path::Component::Normal(name)) = - filename.components().last() + filename.components().next_back() else { panic!("Why is the header variable so weird?") }; @@ -187,7 +187,7 @@ fn compare_generated_header( header.display(), looked_at, ), - }; + } let (builder, roundtrip_builder) = builder.into_builder(check_roundtrip)?; diff --git a/bindgen/codegen/mod.rs b/bindgen/codegen/mod.rs index f6616e7b42..e4e70211ef 100644 --- a/bindgen/codegen/mod.rs +++ b/bindgen/codegen/mod.rs @@ -272,7 +272,7 @@ fn format_attribute_tokens(attrs: &[TokenStream]) -> Vec { comments.push(format!( "{}{}", if block_comment_count > 0 { " " } else { "" }, - cleaned.to_string() + cleaned )); } else if trimmed.starts_with('#') { attrs.push(line.into()); From 63f7863de3590ee34b7bd836cc7dcb787feb7b5b Mon Sep 17 00:00:00 2001 From: oberrich Date: Thu, 6 Feb 2025 13:26:25 +0100 Subject: [PATCH 32/66] fix: improve comment formatting, unnest branch --- bindgen/codegen/mod.rs | 101 +++++++++++++++++++++-------------------- 1 file changed, 51 insertions(+), 50 deletions(-) diff --git a/bindgen/codegen/mod.rs b/bindgen/codegen/mod.rs index e4e70211ef..dcd8709f8b 100644 --- a/bindgen/codegen/mod.rs +++ b/bindgen/codegen/mod.rs @@ -232,64 +232,65 @@ where // ["#[repr(C)]", "#[repr(align(4))]", "#[derive(Debug, Default, Copy, Clone)]"] fn format_attribute_tokens(attrs: &[TokenStream]) -> Vec { if attrs.is_empty() || !attrs.iter().any(|attr| !attr.is_empty()) { - vec![] - } else { - // If this ever errors, we may have to - depending on the attributes - use different dummy items to - // attach the attributes to. This is necessary to get a valid unparse from prettyplease/rustfmt - let attrs_with_body = quote! { - #(#attrs)* - fn body() {} - }; + return vec![]; + } - let mut attrs = vec![]; - let mut comments = vec![]; - let mut block_comment = false; - let mut block_comment_count = 0; + // If this ever errors, we may have to - depending on the attributes - use different dummy items to + // attach the attributes to. This is necessary to get a valid unparse from prettyplease/rustfmt + let attrs_with_body = quote! { + #(#attrs)* + fn body() {} + }; - for line in unparse(&syn::parse_quote!(#attrs_with_body)) - .split('\n') - .take_while(|line| !line.starts_with("fn body")) - .join("\n") - .lines() - { - let trimmed = line.trim(); - if trimmed.starts_with("/*") { - block_comment_count += 1; - block_comment = true; - } - - let cleaned = trimmed - .trim_start_matches('/') - .trim_start_matches('*') - .trim_start_matches('!') - .trim_end_matches('/') - .trim_end_matches('*'); - - if block_comment || - trimmed.starts_with("///") || - trimmed.starts_with("//") - { - comments.push(format!( - "{}{}", - if block_comment_count > 0 { " " } else { "" }, - cleaned - )); - } else if trimmed.starts_with('#') { - attrs.push(line.into()); - } + let mut attrs = vec![]; + let mut comments = vec![]; + let mut block_comment = false; + + for line in unparse(&syn::parse_quote!(#attrs_with_body)) + .split('\n') + .take_while(|line| !line.starts_with("fn body() {}")) + .join("\n") + .lines() + { + let trimmed = line.trim(); + if trimmed.starts_with("/*") { + block_comment = true; + } - if trimmed.ends_with("*/") { - block_comment_count = 0; - block_comment = false; - } + let cleaned = trimmed + .trim_start_matches('/') + .trim_start_matches('*') + .trim_start_matches('!') + .trim_end_matches('/') + .trim_end_matches('*'); + + if block_comment || + trimmed.starts_with("///") || + trimmed.starts_with("//") + { + comments.push(cleaned.to_string()); + } else if trimmed.starts_with('#') { + attrs.push(line.to_string()); } - if !comments.is_empty() && comments.iter().any(|c| !c.is_empty()) { - attrs.insert(0, format!("#[doc = \"{}\"]", comments.join("\n"))); + if trimmed.ends_with("*/") { + block_comment = false; } + } + + let comment = comments + .into_iter() + .filter(|c| !c.is_empty()) + .skip(1) + .map(|c| format!(" {}", c)) + .join("\n"); - attrs + // Only insert the attribute if there are formatted comments + if !comment.is_empty() { + attrs.insert(0, format!("#[doc = \"{}\"]", comment)); } + + attrs } fn process_attributes( From c270044b9c9fe45013cdd3c89d1ad593c4f440a8 Mon Sep 17 00:00:00 2001 From: oberrich Date: Thu, 6 Feb 2025 13:29:34 +0100 Subject: [PATCH 33/66] fix: don't skip empty lines indiscriminately --- bindgen/codegen/mod.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/bindgen/codegen/mod.rs b/bindgen/codegen/mod.rs index dcd8709f8b..00f0d07a9d 100644 --- a/bindgen/codegen/mod.rs +++ b/bindgen/codegen/mod.rs @@ -280,7 +280,6 @@ fn format_attribute_tokens(attrs: &[TokenStream]) -> Vec { let comment = comments .into_iter() - .filter(|c| !c.is_empty()) .skip(1) .map(|c| format!(" {}", c)) .join("\n"); From ec0a0dc2f6fb373599309ef9c30c0df46c13b3b2 Mon Sep 17 00:00:00 2001 From: oberrich Date: Thu, 6 Feb 2025 13:31:59 +0100 Subject: [PATCH 34/66] fix: skip -> take --- bindgen/codegen/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bindgen/codegen/mod.rs b/bindgen/codegen/mod.rs index 00f0d07a9d..081882952c 100644 --- a/bindgen/codegen/mod.rs +++ b/bindgen/codegen/mod.rs @@ -280,7 +280,7 @@ fn format_attribute_tokens(attrs: &[TokenStream]) -> Vec { let comment = comments .into_iter() - .skip(1) + .take(1) .map(|c| format!(" {}", c)) .join("\n"); From 9305a1d7e5d61fc4be5d2df1e28d940bc6f5a5ec Mon Sep 17 00:00:00 2001 From: oberrich Date: Thu, 6 Feb 2025 13:35:16 +0100 Subject: [PATCH 35/66] fix: use itertools for logic --- bindgen/codegen/mod.rs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/bindgen/codegen/mod.rs b/bindgen/codegen/mod.rs index 081882952c..31d3ecab99 100644 --- a/bindgen/codegen/mod.rs +++ b/bindgen/codegen/mod.rs @@ -278,12 +278,11 @@ fn format_attribute_tokens(attrs: &[TokenStream]) -> Vec { } } - let comment = comments - .into_iter() - .take(1) - .map(|c| format!(" {}", c)) - .join("\n"); - + let comment = itertools::interleave( + comments.iter().take(1).map(|c| c.to_string()), + comments.iter().skip(1).map(|c| format!(" {}", c)), + ) + .join("\n"); // Only insert the attribute if there are formatted comments if !comment.is_empty() { attrs.insert(0, format!("#[doc = \"{}\"]", comment)); From 67ef11f6212e37ead1b45bd4d9eb46e943311d97 Mon Sep 17 00:00:00 2001 From: oberrich Date: Thu, 6 Feb 2025 13:39:44 +0100 Subject: [PATCH 36/66] fix: attempt to not trim whitespaces --- bindgen/codegen/mod.rs | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/bindgen/codegen/mod.rs b/bindgen/codegen/mod.rs index 31d3ecab99..6c7a5ca397 100644 --- a/bindgen/codegen/mod.rs +++ b/bindgen/codegen/mod.rs @@ -252,12 +252,11 @@ fn format_attribute_tokens(attrs: &[TokenStream]) -> Vec { .join("\n") .lines() { - let trimmed = line.trim(); - if trimmed.starts_with("/*") { + if line.starts_with("/*") { block_comment = true; } - let cleaned = trimmed + let cleaned = line .trim_start_matches('/') .trim_start_matches('*') .trim_start_matches('!') @@ -265,8 +264,8 @@ fn format_attribute_tokens(attrs: &[TokenStream]) -> Vec { .trim_end_matches('*'); if block_comment || - trimmed.starts_with("///") || - trimmed.starts_with("//") + line.starts_with("///") || + line.starts_with("//") { comments.push(cleaned.to_string()); } else if trimmed.starts_with('#') { @@ -278,11 +277,8 @@ fn format_attribute_tokens(attrs: &[TokenStream]) -> Vec { } } - let comment = itertools::interleave( - comments.iter().take(1).map(|c| c.to_string()), - comments.iter().skip(1).map(|c| format!(" {}", c)), - ) - .join("\n"); + let comment = comments.join("\n"); + // Only insert the attribute if there are formatted comments if !comment.is_empty() { attrs.insert(0, format!("#[doc = \"{}\"]", comment)); From 031be86abb0a3a8b8c41089e901cbb061a286518 Mon Sep 17 00:00:00 2001 From: oberrich Date: Thu, 6 Feb 2025 13:40:47 +0100 Subject: [PATCH 37/66] fix: syntax error --- bindgen/codegen/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bindgen/codegen/mod.rs b/bindgen/codegen/mod.rs index 6c7a5ca397..759c144090 100644 --- a/bindgen/codegen/mod.rs +++ b/bindgen/codegen/mod.rs @@ -268,11 +268,11 @@ fn format_attribute_tokens(attrs: &[TokenStream]) -> Vec { line.starts_with("//") { comments.push(cleaned.to_string()); - } else if trimmed.starts_with('#') { + } else if line.starts_with('#') { attrs.push(line.to_string()); } - if trimmed.ends_with("*/") { + if line.ends_with("*/") { block_comment = false; } } From dd80133243b69d4bd37b060ca00cc38a9a729591 Mon Sep 17 00:00:00 2001 From: oberrich Date: Thu, 6 Feb 2025 13:49:55 +0100 Subject: [PATCH 38/66] chore: run rustfmt on `codegen/mod.rs` --- bindgen/codegen/mod.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/bindgen/codegen/mod.rs b/bindgen/codegen/mod.rs index 759c144090..23481f3451 100644 --- a/bindgen/codegen/mod.rs +++ b/bindgen/codegen/mod.rs @@ -263,10 +263,7 @@ fn format_attribute_tokens(attrs: &[TokenStream]) -> Vec { .trim_end_matches('/') .trim_end_matches('*'); - if block_comment || - line.starts_with("///") || - line.starts_with("//") - { + if block_comment || line.starts_with("///") || line.starts_with("//") { comments.push(cleaned.to_string()); } else if line.starts_with('#') { attrs.push(line.to_string()); From e1d3855a4fc1e988f8eb946807a6728ddfa4e182 Mon Sep 17 00:00:00 2001 From: oberrich Date: Thu, 6 Feb 2025 14:00:32 +0100 Subject: [PATCH 39/66] fix: filter out `#[must_use]` attributes --- bindgen/codegen/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/bindgen/codegen/mod.rs b/bindgen/codegen/mod.rs index 23481f3451..d8476198bf 100644 --- a/bindgen/codegen/mod.rs +++ b/bindgen/codegen/mod.rs @@ -1152,6 +1152,7 @@ impl CodeGenerator for Type { }; let mut attrs = attrs_for_item(item, ctx); + attrs.retain(|attr| attr.to_string() == attributes::must_use().to_string()); if let Some(inner_attrs) = result.get_attributes(inner_item.id()) From c4c53585ded9f6026865ff087d2a75ba8cc848ca Mon Sep 17 00:00:00 2001 From: oberrich Date: Thu, 6 Feb 2025 14:01:16 +0100 Subject: [PATCH 40/66] fix: invert logic --- bindgen/codegen/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bindgen/codegen/mod.rs b/bindgen/codegen/mod.rs index d8476198bf..c34a30bba6 100644 --- a/bindgen/codegen/mod.rs +++ b/bindgen/codegen/mod.rs @@ -1152,7 +1152,7 @@ impl CodeGenerator for Type { }; let mut attrs = attrs_for_item(item, ctx); - attrs.retain(|attr| attr.to_string() == attributes::must_use().to_string()); + attrs.retain(|attr| attr.to_string() |= attributes::must_use().to_string()); if let Some(inner_attrs) = result.get_attributes(inner_item.id()) From 9dcbd24758a199aa02d2df8517cd1960c4269d53 Mon Sep 17 00:00:00 2001 From: oberrich Date: Thu, 6 Feb 2025 14:03:02 +0100 Subject: [PATCH 41/66] fix: invert operator --- bindgen/codegen/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bindgen/codegen/mod.rs b/bindgen/codegen/mod.rs index c34a30bba6..c97cd19572 100644 --- a/bindgen/codegen/mod.rs +++ b/bindgen/codegen/mod.rs @@ -1152,7 +1152,7 @@ impl CodeGenerator for Type { }; let mut attrs = attrs_for_item(item, ctx); - attrs.retain(|attr| attr.to_string() |= attributes::must_use().to_string()); + attrs.retain(|attr| attr.to_string() != attributes::must_use().to_string()); if let Some(inner_attrs) = result.get_attributes(inner_item.id()) From 9c02ec5de546243d968c283425fedcf9a473cf94 Mon Sep 17 00:00:00 2001 From: oberrich Date: Thu, 6 Feb 2025 14:11:17 +0100 Subject: [PATCH 42/66] fix(tests): apply new `#[must_use]` order --- .../tests/expectations/tests/func_return_must_use.rs | 4 ++-- .../tests/expectations/tests/issue-710-must-use-type.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/bindgen-tests/tests/expectations/tests/func_return_must_use.rs b/bindgen-tests/tests/expectations/tests/func_return_must_use.rs index 904c71cbe3..aa640f2ae2 100644 --- a/bindgen-tests/tests/expectations/tests/func_return_must_use.rs +++ b/bindgen-tests/tests/expectations/tests/func_return_must_use.rs @@ -4,9 +4,9 @@ unsafe extern "C" { #[must_use] pub fn return_int() -> MustUseInt; } +#[must_use] #[repr(C)] #[derive(Debug, Copy, Clone)] -#[must_use] pub struct MustUseStruct { _unused: [u8; 0], } @@ -24,9 +24,9 @@ unsafe extern "C" { pub fn return_plain_int() -> ::std::os::raw::c_int; } ///
+#[must_use] #[repr(C)] #[derive(Debug, Default, Copy, Clone)] -#[must_use] pub struct AnnotatedStruct {} #[allow(clippy::unnecessary_operation, clippy::identity_op)] const _: () = { diff --git a/bindgen-tests/tests/expectations/tests/issue-710-must-use-type.rs b/bindgen-tests/tests/expectations/tests/issue-710-must-use-type.rs index d7006dd011..c00bdba1f5 100644 --- a/bindgen-tests/tests/expectations/tests/issue-710-must-use-type.rs +++ b/bindgen-tests/tests/expectations/tests/issue-710-must-use-type.rs @@ -1,14 +1,14 @@ #![allow(dead_code, non_snake_case, non_camel_case_types, non_upper_case_globals)] +#[must_use] #[repr(C)] #[derive(Debug, Copy, Clone)] -#[must_use] pub struct A { _unused: [u8; 0], } ///
+#[must_use] #[repr(C)] #[derive(Debug, Copy, Clone)] -#[must_use] pub struct B { _unused: [u8; 0], } From d6463458d2a898dda4e27bff1459a0afb86a7862 Mon Sep 17 00:00:00 2001 From: oberrich Date: Thu, 6 Feb 2025 14:11:59 +0100 Subject: [PATCH 43/66] chore: run rustfmt on `codegen/mod.rs` --- bindgen/codegen/mod.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/bindgen/codegen/mod.rs b/bindgen/codegen/mod.rs index c97cd19572..fffa9b4aa4 100644 --- a/bindgen/codegen/mod.rs +++ b/bindgen/codegen/mod.rs @@ -1152,7 +1152,9 @@ impl CodeGenerator for Type { }; let mut attrs = attrs_for_item(item, ctx); - attrs.retain(|attr| attr.to_string() != attributes::must_use().to_string()); + attrs.retain(|attr| { + attr.to_string() != attributes::must_use().to_string() + }); if let Some(inner_attrs) = result.get_attributes(inner_item.id()) From c7d089026f8529a0031603fb52cc854a53a9792a Mon Sep 17 00:00:00 2001 From: oberrich Date: Thu, 6 Feb 2025 14:34:13 +0100 Subject: [PATCH 44/66] fix: test to apply attrs for function_item --- bindgen-tests/tests/expectations/tests/attribute-custom-cli.rs | 2 +- bindgen/codegen/mod.rs | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/bindgen-tests/tests/expectations/tests/attribute-custom-cli.rs b/bindgen-tests/tests/expectations/tests/attribute-custom-cli.rs index 82179b2840..0340e9b8b3 100644 --- a/bindgen-tests/tests/expectations/tests/attribute-custom-cli.rs +++ b/bindgen-tests/tests/expectations/tests/attribute-custom-cli.rs @@ -14,8 +14,8 @@ const _: () = { ][::std::mem::offset_of!(foo_struct, inner) - 0usize]; }; #[repr(u32)] -#[cfg_attr(test, derive(PartialOrd, Copy))] #[derive(Clone, Hash, PartialEq, Eq)] +#[cfg_attr(test, derive(PartialOrd, Copy))] pub enum foo_enum { inner = 0, } diff --git a/bindgen/codegen/mod.rs b/bindgen/codegen/mod.rs index fffa9b4aa4..9b3bdc1d85 100644 --- a/bindgen/codegen/mod.rs +++ b/bindgen/codegen/mod.rs @@ -3307,7 +3307,8 @@ impl Method { let block = ctx.wrap_unsafe_ops(quote! ( #( #stmts );*)); - let mut attrs = vec![attributes::inline()]; + let mut attrs = attrs_for_item(function_item, ctx); + attrs.push(attributes::inline()); if signature.must_use() { attrs.push(attributes::must_use()); From de6805e2fe2a0dc6cb3215609643bfb4689ae78a Mon Sep 17 00:00:00 2001 From: oberrich Date: Thu, 6 Feb 2025 14:38:14 +0100 Subject: [PATCH 45/66] fix: remaining expectations related to new attribute handling --- bindgen-tests/tests/expectations/tests/enum-doc-bitfield.rs | 2 +- bindgen-tests/tests/expectations/tests/enum-doc-mod.rs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/bindgen-tests/tests/expectations/tests/enum-doc-bitfield.rs b/bindgen-tests/tests/expectations/tests/enum-doc-bitfield.rs index ba73a8ea3e..bf00f0dbc6 100644 --- a/bindgen-tests/tests/expectations/tests/enum-doc-bitfield.rs +++ b/bindgen-tests/tests/expectations/tests/enum-doc-bitfield.rs @@ -52,7 +52,7 @@ impl ::std::ops::BitAndAssign for B { self.0 &= rhs.0; } } -#[repr(transparent)] /// Document enum +#[repr(transparent)] #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] pub struct B(pub ::std::os::raw::c_uint); diff --git a/bindgen-tests/tests/expectations/tests/enum-doc-mod.rs b/bindgen-tests/tests/expectations/tests/enum-doc-mod.rs index 2b18b35df0..d3e2cbfe08 100644 --- a/bindgen-tests/tests/expectations/tests/enum-doc-mod.rs +++ b/bindgen-tests/tests/expectations/tests/enum-doc-mod.rs @@ -1,4 +1,5 @@ #![allow(dead_code, non_snake_case, non_camel_case_types, non_upper_case_globals)] +/// Document enum pub mod B { /// Document enum pub type Type = ::std::os::raw::c_uint; From 1caa01a8949a9c2d8d283e004531b28ec53c0be5 Mon Sep 17 00:00:00 2001 From: oberrich Date: Thu, 6 Feb 2025 14:38:27 +0100 Subject: [PATCH 46/66] chore: run rustfmt on `codegen/mod.rs` --- bindgen/codegen/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bindgen/codegen/mod.rs b/bindgen/codegen/mod.rs index 9b3bdc1d85..b09edcec7e 100644 --- a/bindgen/codegen/mod.rs +++ b/bindgen/codegen/mod.rs @@ -3307,7 +3307,7 @@ impl Method { let block = ctx.wrap_unsafe_ops(quote! ( #( #stmts );*)); - let mut attrs = attrs_for_item(function_item, ctx); + let mut attrs = attrs_for_item(function_item, ctx); attrs.push(attributes::inline()); if signature.must_use() { From 6fe99c94a934f5255345ae5cda490f3f470d5529 Mon Sep 17 00:00:00 2001 From: oberrich Date: Thu, 6 Feb 2025 14:40:02 +0100 Subject: [PATCH 47/66] chore: apply clippy fix --- bindgen/codegen/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bindgen/codegen/mod.rs b/bindgen/codegen/mod.rs index b09edcec7e..b8a6da3cbc 100644 --- a/bindgen/codegen/mod.rs +++ b/bindgen/codegen/mod.rs @@ -278,7 +278,7 @@ fn format_attribute_tokens(attrs: &[TokenStream]) -> Vec { // Only insert the attribute if there are formatted comments if !comment.is_empty() { - attrs.insert(0, format!("#[doc = \"{}\"]", comment)); + attrs.insert(0, format!("#[doc = \"{comment}\"]")); } attrs From a4f583ddf4908878d4696fb9d75c3922deded7db Mon Sep 17 00:00:00 2001 From: oberrich Date: Thu, 6 Feb 2025 14:45:34 +0100 Subject: [PATCH 48/66] fix: attempt to fix CI --- bindgen/codegen/mod.rs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/bindgen/codegen/mod.rs b/bindgen/codegen/mod.rs index b8a6da3cbc..9dc87ffcec 100644 --- a/bindgen/codegen/mod.rs +++ b/bindgen/codegen/mod.rs @@ -278,7 +278,7 @@ fn format_attribute_tokens(attrs: &[TokenStream]) -> Vec { // Only insert the attribute if there are formatted comments if !comment.is_empty() { - attrs.insert(0, format!("#[doc = \"{comment}\"]")); + attrs.insert(0, format!("#[doc = \"{}\"]", comment)); } attrs @@ -1152,9 +1152,17 @@ impl CodeGenerator for Type { }; let mut attrs = attrs_for_item(item, ctx); - attrs.retain(|attr| { + /*attrs.retain(|attr| { attr.to_string() != attributes::must_use().to_string() }); + TODO: See if this disappears: + +++ generated from: "/home/runner/work/rust-bindgen/rust-bindgen/bindgen-tests/tests/headers/dynamic_loading_attributes.h" +24 24 | let baz = __library.get(b"baz\0").map(|sym| *sym)?; +25 25 | Ok(TestLib { __library, foo, baz }) +26 26 | } +27 | - #[must_use] +28 27 | /** @brief A function + */ if let Some(inner_attrs) = result.get_attributes(inner_item.id()) @@ -3307,7 +3315,7 @@ impl Method { let block = ctx.wrap_unsafe_ops(quote! ( #( #stmts );*)); - let mut attrs = attrs_for_item(function_item, ctx); + let mut attrs = attrs_for_item(function_item, ctx); attrs.push(attributes::inline()); if signature.must_use() { From ffaec449f1c91340e200a36e2173df2245b0fcf6 Mon Sep 17 00:00:00 2001 From: oberrich Date: Thu, 6 Feb 2025 14:47:48 +0100 Subject: [PATCH 49/66] fix: terminate comment --- bindgen/codegen/mod.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/bindgen/codegen/mod.rs b/bindgen/codegen/mod.rs index 9dc87ffcec..9204228fcc 100644 --- a/bindgen/codegen/mod.rs +++ b/bindgen/codegen/mod.rs @@ -1161,7 +1161,6 @@ impl CodeGenerator for Type { 25 25 | Ok(TestLib { __library, foo, baz }) 26 26 | } 27 | - #[must_use] -28 27 | /** @brief A function */ if let Some(inner_attrs) = From f27db9ad4acc7a5e5f6e8336afdf2b2f95331b77 Mon Sep 17 00:00:00 2001 From: oberrich Date: Thu, 6 Feb 2025 14:54:57 +0100 Subject: [PATCH 50/66] fix: attempt to fix ci --- bindgen/codegen/mod.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/bindgen/codegen/mod.rs b/bindgen/codegen/mod.rs index 9204228fcc..d83b8c698e 100644 --- a/bindgen/codegen/mod.rs +++ b/bindgen/codegen/mod.rs @@ -4785,6 +4785,10 @@ impl CodeGenerator for Function { let mut attrs = attrs_for_item(item, ctx); + attrs.retain(|attr| { + attr.to_string() != attributes::must_use().to_string() + }); + // Resolve #[must_use] attribute through return type if signature .return_type() From fad0ff594917882ce72e2e28b9890910611eb719 Mon Sep 17 00:00:00 2001 From: oberrich Date: Thu, 6 Feb 2025 14:58:57 +0100 Subject: [PATCH 51/66] fix: attempt to fix ci --- bindgen/codegen/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/bindgen/codegen/mod.rs b/bindgen/codegen/mod.rs index d83b8c698e..edf0cf59d8 100644 --- a/bindgen/codegen/mod.rs +++ b/bindgen/codegen/mod.rs @@ -4794,6 +4794,7 @@ impl CodeGenerator for Function { .return_type() .into_resolver() .through_type_refs() + .through_type_aliases() .resolve(ctx) .must_use(ctx) { From d0652a0b37106a35c7ed4c5f749206f7ea95e5c4 Mon Sep 17 00:00:00 2001 From: oberrich Date: Thu, 6 Feb 2025 15:01:27 +0100 Subject: [PATCH 52/66] fix: attempt to fix ci --- bindgen/codegen/mod.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/bindgen/codegen/mod.rs b/bindgen/codegen/mod.rs index edf0cf59d8..e818345ab9 100644 --- a/bindgen/codegen/mod.rs +++ b/bindgen/codegen/mod.rs @@ -3316,6 +3316,10 @@ impl Method { let mut attrs = attrs_for_item(function_item, ctx); attrs.push(attributes::inline()); + + attrs.retain(|attr| { + attr.to_string() != attributes::must_use().to_string() + }); if signature.must_use() { attrs.push(attributes::must_use()); @@ -4794,7 +4798,6 @@ impl CodeGenerator for Function { .return_type() .into_resolver() .through_type_refs() - .through_type_aliases() .resolve(ctx) .must_use(ctx) { From b625a691c3551d1cf6b4878729b1d96f4c6c2632 Mon Sep 17 00:00:00 2001 From: oberrich Date: Thu, 6 Feb 2025 15:08:35 +0100 Subject: [PATCH 53/66] fix: only remove `#[must_use]` for non-dynamic fns --- bindgen/codegen/mod.rs | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/bindgen/codegen/mod.rs b/bindgen/codegen/mod.rs index e818345ab9..105f31eab4 100644 --- a/bindgen/codegen/mod.rs +++ b/bindgen/codegen/mod.rs @@ -1153,15 +1153,15 @@ impl CodeGenerator for Type { let mut attrs = attrs_for_item(item, ctx); /*attrs.retain(|attr| { - attr.to_string() != attributes::must_use().to_string() - }); - TODO: See if this disappears: - +++ generated from: "/home/runner/work/rust-bindgen/rust-bindgen/bindgen-tests/tests/headers/dynamic_loading_attributes.h" -24 24 | let baz = __library.get(b"baz\0").map(|sym| *sym)?; -25 25 | Ok(TestLib { __library, foo, baz }) -26 26 | } -27 | - #[must_use] - */ + attr.to_string() != attributes::must_use().to_string() + }); + TODO: See if this disappears: + +++ generated from: "/home/runner/work/rust-bindgen/rust-bindgen/bindgen-tests/tests/headers/dynamic_loading_attributes.h" + 24 24 | let baz = __library.get(b"baz\0").map(|sym| *sym)?; + 25 25 | Ok(TestLib { __library, foo, baz }) + 26 26 | } + 27 | - #[must_use] + */ if let Some(inner_attrs) = result.get_attributes(inner_item.id()) @@ -3314,9 +3314,9 @@ impl Method { let block = ctx.wrap_unsafe_ops(quote! ( #( #stmts );*)); - let mut attrs = attrs_for_item(function_item, ctx); + let mut attrs = attrs_for_item(function_item, ctx); attrs.push(attributes::inline()); - + attrs.retain(|attr| { attr.to_string() != attributes::must_use().to_string() }); @@ -4789,9 +4789,6 @@ impl CodeGenerator for Function { let mut attrs = attrs_for_item(item, ctx); - attrs.retain(|attr| { - attr.to_string() != attributes::must_use().to_string() - }); // Resolve #[must_use] attribute through return type if signature @@ -4907,6 +4904,12 @@ impl CodeGenerator for Function { .rust_features .unsafe_extern_blocks .then(|| quote!(unsafe)); + + if !is_dynamic_function { + attrs.retain(|attr| { + attr.to_string() != attributes::must_use().to_string() + }); + } let attrs = process_attributes( result, From 726ff75734e2cfe33addb338214f9cdcdc62241d Mon Sep 17 00:00:00 2001 From: oberrich Date: Thu, 6 Feb 2025 15:10:23 +0100 Subject: [PATCH 54/66] chore: run rustfmt on `codegen/mod.rs` --- bindgen/codegen/mod.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/bindgen/codegen/mod.rs b/bindgen/codegen/mod.rs index 105f31eab4..4bc43c36eb 100644 --- a/bindgen/codegen/mod.rs +++ b/bindgen/codegen/mod.rs @@ -4789,7 +4789,6 @@ impl CodeGenerator for Function { let mut attrs = attrs_for_item(item, ctx); - // Resolve #[must_use] attribute through return type if signature .return_type() @@ -4904,7 +4903,7 @@ impl CodeGenerator for Function { .rust_features .unsafe_extern_blocks .then(|| quote!(unsafe)); - + if !is_dynamic_function { attrs.retain(|attr| { attr.to_string() != attributes::must_use().to_string() From f7eeecdeb357cdceb49e909f9c2b7d9db2e7d47c Mon Sep 17 00:00:00 2001 From: oberrich Date: Thu, 6 Feb 2025 15:12:36 +0100 Subject: [PATCH 55/66] fix: attempt to fix ci --- bindgen/codegen/mod.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/bindgen/codegen/mod.rs b/bindgen/codegen/mod.rs index 4bc43c36eb..f48e1ca831 100644 --- a/bindgen/codegen/mod.rs +++ b/bindgen/codegen/mod.rs @@ -1152,9 +1152,10 @@ impl CodeGenerator for Type { }; let mut attrs = attrs_for_item(item, ctx); - /*attrs.retain(|attr| { + attrs.retain(|attr| { attr.to_string() != attributes::must_use().to_string() }); + /* TODO: See if this disappears: +++ generated from: "/home/runner/work/rust-bindgen/rust-bindgen/bindgen-tests/tests/headers/dynamic_loading_attributes.h" 24 24 | let baz = __library.get(b"baz\0").map(|sym| *sym)?; @@ -4904,7 +4905,7 @@ impl CodeGenerator for Function { .unsafe_extern_blocks .then(|| quote!(unsafe)); - if !is_dynamic_function { + if is_dynamic_function { attrs.retain(|attr| { attr.to_string() != attributes::must_use().to_string() }); From 00663bf4496062acb008ace92d643658115b7411 Mon Sep 17 00:00:00 2001 From: oberrich Date: Thu, 6 Feb 2025 15:19:40 +0100 Subject: [PATCH 56/66] fix: attempt to fix ci --- bindgen/codegen/mod.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/bindgen/codegen/mod.rs b/bindgen/codegen/mod.rs index f48e1ca831..041b35e32c 100644 --- a/bindgen/codegen/mod.rs +++ b/bindgen/codegen/mod.rs @@ -3259,6 +3259,11 @@ impl Method { ret = quote! { -> Self }; } + + // TODO: Why can't we use + // TODO: let mut exprs = utils::fnsig_arguments(ctx, signature); + // TODO: here instead? + // TODO: Seems like `arguments_from_signature` is a less powerful implementation of that. let mut exprs = helpers::ast_ty::arguments_from_signature(signature, ctx); @@ -3318,9 +3323,10 @@ impl Method { let mut attrs = attrs_for_item(function_item, ctx); attrs.push(attributes::inline()); + /* maybe add to methods afterall? attrs.retain(|attr| { attr.to_string() != attributes::must_use().to_string() - }); + });*/ if signature.must_use() { attrs.push(attributes::must_use()); From e6c45968dcafe6f03bf9373708a240a31c93cd6b Mon Sep 17 00:00:00 2001 From: oberrich Date: Thu, 6 Feb 2025 15:27:20 +0100 Subject: [PATCH 57/66] fix: attempt to fix ci at last --- bindgen/codegen/mod.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/bindgen/codegen/mod.rs b/bindgen/codegen/mod.rs index 041b35e32c..1ecae87e74 100644 --- a/bindgen/codegen/mod.rs +++ b/bindgen/codegen/mod.rs @@ -3322,11 +3322,10 @@ impl Method { let mut attrs = attrs_for_item(function_item, ctx); attrs.push(attributes::inline()); - - /* maybe add to methods afterall? + attrs.retain(|attr| { attr.to_string() != attributes::must_use().to_string() - });*/ + }); if signature.must_use() { attrs.push(attributes::must_use()); @@ -4796,6 +4795,12 @@ impl CodeGenerator for Function { let mut attrs = attrs_for_item(item, ctx); + if !is_dynamic_function { + attrs.retain(|attr| { + attr.to_string() != attributes::must_use().to_string() + }); + } + // Resolve #[must_use] attribute through return type if signature .return_type() @@ -4911,11 +4916,6 @@ impl CodeGenerator for Function { .unsafe_extern_blocks .then(|| quote!(unsafe)); - if is_dynamic_function { - attrs.retain(|attr| { - attr.to_string() != attributes::must_use().to_string() - }); - } let attrs = process_attributes( result, From 05178c6894b6e1cff9476ac6b45b98bb3560228d Mon Sep 17 00:00:00 2001 From: oberrich Date: Thu, 6 Feb 2025 15:34:11 +0100 Subject: [PATCH 58/66] fix: attempt to fix ci - see what happens --- bindgen/codegen/mod.rs | 31 +++++++++++-------------------- 1 file changed, 11 insertions(+), 20 deletions(-) diff --git a/bindgen/codegen/mod.rs b/bindgen/codegen/mod.rs index 1ecae87e74..0722cab4ef 100644 --- a/bindgen/codegen/mod.rs +++ b/bindgen/codegen/mod.rs @@ -324,6 +324,14 @@ fn attrs_for_item(item: &Item, ctx: &BindgenContext) -> Vec { attrs } +fn set_must_use(attrs: &Vec, must_use: bool) { + let must_use_tokens = attributes::must_use(); + attrs.retain(|attr| attr != &must_use_tokens); + if must_use { + attrs.push(must_use_tokens); + } +} + struct WrapAsVariadic { new_name: String, idx_of_va_list_arg: usize, @@ -3322,14 +3330,8 @@ impl Method { let mut attrs = attrs_for_item(function_item, ctx); attrs.push(attributes::inline()); - - attrs.retain(|attr| { - attr.to_string() != attributes::must_use().to_string() - }); - if signature.must_use() { - attrs.push(attributes::must_use()); - } + set_must_use(attrs, signature.must_use()); let attrs = process_attributes( result, @@ -4794,23 +4796,12 @@ impl CodeGenerator for Function { } let mut attrs = attrs_for_item(item, ctx); - - if !is_dynamic_function { - attrs.retain(|attr| { - attr.to_string() != attributes::must_use().to_string() - }); - } - - // Resolve #[must_use] attribute through return type - if signature + set_must_use(attrs, signature .return_type() .into_resolver() .through_type_refs() .resolve(ctx) - .must_use(ctx) - { - attrs.push(attributes::must_use()); - } + .must_use(ctx)); let abi = match signature.abi(ctx, Some(name)) { Err(err) => { From cd86a8f363b64f84d349f96e6669409fa02dc998 Mon Sep 17 00:00:00 2001 From: oberrich Date: Thu, 6 Feb 2025 15:42:07 +0100 Subject: [PATCH 59/66] fix: make it compile --- bindgen/codegen/mod.rs | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/bindgen/codegen/mod.rs b/bindgen/codegen/mod.rs index 0722cab4ef..64b1be6059 100644 --- a/bindgen/codegen/mod.rs +++ b/bindgen/codegen/mod.rs @@ -324,9 +324,10 @@ fn attrs_for_item(item: &Item, ctx: &BindgenContext) -> Vec { attrs } -fn set_must_use(attrs: &Vec, must_use: bool) { +fn set_must_use(attrs: &mut Vec, must_use: bool) { let must_use_tokens = attributes::must_use(); - attrs.retain(|attr| attr != &must_use_tokens); + let must_use_str = must_use_tokens.to_string(); + attrs.retain(|attr| attr.to_string() == must_use_str); if must_use { attrs.push(must_use_tokens); } @@ -1161,9 +1162,9 @@ impl CodeGenerator for Type { let mut attrs = attrs_for_item(item, ctx); attrs.retain(|attr| { - attr.to_string() != attributes::must_use().to_string() - }); - /* + attr.to_string() != attributes::must_use().to_string() + }); + /* TODO: See if this disappears: +++ generated from: "/home/runner/work/rust-bindgen/rust-bindgen/bindgen-tests/tests/headers/dynamic_loading_attributes.h" 24 24 | let baz = __library.get(b"baz\0").map(|sym| *sym)?; @@ -3267,7 +3268,6 @@ impl Method { ret = quote! { -> Self }; } - // TODO: Why can't we use // TODO: let mut exprs = utils::fnsig_arguments(ctx, signature); // TODO: here instead? @@ -3331,7 +3331,7 @@ impl Method { let mut attrs = attrs_for_item(function_item, ctx); attrs.push(attributes::inline()); - set_must_use(attrs, signature.must_use()); + set_must_use(&mut attrs, signature.must_use()); let attrs = process_attributes( result, @@ -4796,12 +4796,15 @@ impl CodeGenerator for Function { } let mut attrs = attrs_for_item(item, ctx); - set_must_use(attrs, signature - .return_type() - .into_resolver() - .through_type_refs() - .resolve(ctx) - .must_use(ctx)); + set_must_use( + &mut attrs, + signature + .return_type() + .into_resolver() + .through_type_refs() + .resolve(ctx) + .must_use(ctx), + ); let abi = match signature.abi(ctx, Some(name)) { Err(err) => { @@ -4907,7 +4910,6 @@ impl CodeGenerator for Function { .unsafe_extern_blocks .then(|| quote!(unsafe)); - let attrs = process_attributes( result, item, From a0c642ebc2b970b4e69857216502847a23e49f4b Mon Sep 17 00:00:00 2001 From: oberrich Date: Thu, 6 Feb 2025 15:44:53 +0100 Subject: [PATCH 60/66] fix: invert condition --- bindgen/codegen/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bindgen/codegen/mod.rs b/bindgen/codegen/mod.rs index 64b1be6059..cf0c468e64 100644 --- a/bindgen/codegen/mod.rs +++ b/bindgen/codegen/mod.rs @@ -327,7 +327,7 @@ fn attrs_for_item(item: &Item, ctx: &BindgenContext) -> Vec { fn set_must_use(attrs: &mut Vec, must_use: bool) { let must_use_tokens = attributes::must_use(); let must_use_str = must_use_tokens.to_string(); - attrs.retain(|attr| attr.to_string() == must_use_str); + attrs.retain(|attr| attr.to_string() != must_use_str); if must_use { attrs.push(must_use_tokens); } From 973cca19d3c8fe9bc4edc3584ab75ee24b7c8be2 Mon Sep 17 00:00:00 2001 From: oberrich Date: Thu, 6 Feb 2025 15:50:39 +0100 Subject: [PATCH 61/66] fix: attempt to fix ci --- bindgen/codegen/mod.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/bindgen/codegen/mod.rs b/bindgen/codegen/mod.rs index cf0c468e64..5c4158d68a 100644 --- a/bindgen/codegen/mod.rs +++ b/bindgen/codegen/mod.rs @@ -3330,8 +3330,10 @@ impl Method { let mut attrs = attrs_for_item(function_item, ctx); attrs.push(attributes::inline()); - - set_must_use(&mut attrs, signature.must_use()); + if signature.must_use() { + attrs.push(must_use_tokens); + } + //set_must_use(&mut attrs, signature.must_use()); let attrs = process_attributes( result, From f2aecce581de0ced26aef1c3651674d5f4bb5dff Mon Sep 17 00:00:00 2001 From: oberrich Date: Thu, 6 Feb 2025 15:52:07 +0100 Subject: [PATCH 62/66] fix: setting attributes on dynamic_functions seems bad, so let's not do it --- bindgen/codegen/mod.rs | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/bindgen/codegen/mod.rs b/bindgen/codegen/mod.rs index 5c4158d68a..279f33c237 100644 --- a/bindgen/codegen/mod.rs +++ b/bindgen/codegen/mod.rs @@ -4798,15 +4798,18 @@ impl CodeGenerator for Function { } let mut attrs = attrs_for_item(item, ctx); - set_must_use( - &mut attrs, - signature - .return_type() - .into_resolver() - .through_type_refs() - .resolve(ctx) - .must_use(ctx), - ); + + if !is_dynamic_function { + set_must_use( + &mut attrs, + signature + .return_type() + .into_resolver() + .through_type_refs() + .resolve(ctx) + .must_use(ctx), + ); + } let abi = match signature.abi(ctx, Some(name)) { Err(err) => { From d5c134b3a8e60c6fb2b4ff1ff1d9b336c3afb98a Mon Sep 17 00:00:00 2001 From: oberrich Date: Thu, 6 Feb 2025 15:53:10 +0100 Subject: [PATCH 63/66] fix: syntax errors --- bindgen/codegen/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bindgen/codegen/mod.rs b/bindgen/codegen/mod.rs index 279f33c237..b691fb89a8 100644 --- a/bindgen/codegen/mod.rs +++ b/bindgen/codegen/mod.rs @@ -3331,7 +3331,7 @@ impl Method { let mut attrs = attrs_for_item(function_item, ctx); attrs.push(attributes::inline()); if signature.must_use() { - attrs.push(must_use_tokens); + attrs.push(attributes::must_use()); } //set_must_use(&mut attrs, signature.must_use()); From 0698c4f97d6763cacb9b025f6486349581f795a4 Mon Sep 17 00:00:00 2001 From: oberrich Date: Thu, 6 Feb 2025 15:55:28 +0100 Subject: [PATCH 64/66] fix: try the helper again --- bindgen/codegen/mod.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/bindgen/codegen/mod.rs b/bindgen/codegen/mod.rs index b691fb89a8..69ea82759b 100644 --- a/bindgen/codegen/mod.rs +++ b/bindgen/codegen/mod.rs @@ -3330,10 +3330,11 @@ impl Method { let mut attrs = attrs_for_item(function_item, ctx); attrs.push(attributes::inline()); - if signature.must_use() { + + /*if signature.must_use() { attrs.push(attributes::must_use()); - } - //set_must_use(&mut attrs, signature.must_use()); + }*/ + set_must_use(&mut attrs, signature.must_use()); let attrs = process_attributes( result, From c9c772bb0a98e98a07ee7fc8be11c47403c77b8d Mon Sep 17 00:00:00 2001 From: oberrich Date: Thu, 6 Feb 2025 16:04:06 +0100 Subject: [PATCH 65/66] fix: try to combine the attrs --- bindgen/Cargo.toml | 2 +- bindgen/codegen/mod.rs | 15 +++++++++------ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/bindgen/Cargo.toml b/bindgen/Cargo.toml index f3822c2b07..2c25385a1d 100644 --- a/bindgen/Cargo.toml +++ b/bindgen/Cargo.toml @@ -16,7 +16,7 @@ readme = "../README.md" repository = "https://github.com/rust-lang/rust-bindgen" documentation = "https://docs.rs/bindgen" homepage = "https://rust-lang.github.io/rust-bindgen/" -version = "0.71.1" +version = "0.69.5" build = "build.rs" rust-version.workspace = true edition.workspace = true diff --git a/bindgen/codegen/mod.rs b/bindgen/codegen/mod.rs index 69ea82759b..e7dc1d4262 100644 --- a/bindgen/codegen/mod.rs +++ b/bindgen/codegen/mod.rs @@ -324,12 +324,17 @@ fn attrs_for_item(item: &Item, ctx: &BindgenContext) -> Vec { attrs } -fn set_must_use(attrs: &mut Vec, must_use: bool) { +fn combine_must_use(attrs: &mut Vec, must_use: bool) { let must_use_tokens = attributes::must_use(); let must_use_str = must_use_tokens.to_string(); + let before = attrs.len(); attrs.retain(|attr| attr.to_string() != must_use_str); + let after = attrs.len(); + if must_use { attrs.push(must_use_tokens); + } else if before != after { + attrs.push(must_use_tokens); } } @@ -3330,11 +3335,11 @@ impl Method { let mut attrs = attrs_for_item(function_item, ctx); attrs.push(attributes::inline()); - + /*if signature.must_use() { attrs.push(attributes::must_use()); }*/ - set_must_use(&mut attrs, signature.must_use()); + combine_must_use(&mut attrs, signature.must_use()); let attrs = process_attributes( result, @@ -4800,8 +4805,7 @@ impl CodeGenerator for Function { let mut attrs = attrs_for_item(item, ctx); - if !is_dynamic_function { - set_must_use( + combine_must_use( &mut attrs, signature .return_type() @@ -4810,7 +4814,6 @@ impl CodeGenerator for Function { .resolve(ctx) .must_use(ctx), ); - } let abi = match signature.abi(ctx, Some(name)) { Err(err) => { From e95c7e5497a5943eceaae6a1f08f1df3b2d9b2db Mon Sep 17 00:00:00 2001 From: oberrich Date: Thu, 6 Feb 2025 16:05:39 +0100 Subject: [PATCH 66/66] fix: version number --- bindgen/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bindgen/Cargo.toml b/bindgen/Cargo.toml index 2c25385a1d..f3822c2b07 100644 --- a/bindgen/Cargo.toml +++ b/bindgen/Cargo.toml @@ -16,7 +16,7 @@ readme = "../README.md" repository = "https://github.com/rust-lang/rust-bindgen" documentation = "https://docs.rs/bindgen" homepage = "https://rust-lang.github.io/rust-bindgen/" -version = "0.69.5" +version = "0.71.1" build = "build.rs" rust-version.workspace = true edition.workspace = true