diff --git a/Cargo.toml b/Cargo.toml index d403305af..52af5eba6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,7 +28,6 @@ categories = [ [workspace.lints.rust] elided_lifetimes_in_paths = "warn" missing_copy_implementations = "warn" -missing_debug_implementations = "warn" non_ascii_idents = "deny" unreachable_pub = "warn" unsafe_op_in_unsafe_fn = "deny" diff --git a/crates/block2/src/lib.rs b/crates/block2/src/lib.rs index 55978e473..120a3bdcf 100644 --- a/crates/block2/src/lib.rs +++ b/crates/block2/src/lib.rs @@ -297,6 +297,7 @@ #![no_std] #![warn(missing_docs)] +#![warn(missing_debug_implementations)] #![warn(clippy::missing_errors_doc)] #![warn(clippy::missing_panics_doc)] // Update in Cargo.toml as well. diff --git a/crates/dispatch2/src/lib.rs b/crates/dispatch2/src/lib.rs index 7eb2066ef..69f257d53 100644 --- a/crates/dispatch2/src/lib.rs +++ b/crates/dispatch2/src/lib.rs @@ -20,6 +20,7 @@ #![no_std] #![allow(unreachable_patterns)] #![warn(missing_docs)] +#![warn(missing_debug_implementations)] #![warn(clippy::undocumented_unsafe_blocks)] #![warn(clippy::missing_safety_doc)] // Update in Cargo.toml as well. diff --git a/crates/dispatch2/translation-config.toml b/crates/dispatch2/translation-config.toml index e81aa94bf..aa0ef73ab 100644 --- a/crates/dispatch2/translation-config.toml +++ b/crates/dispatch2/translation-config.toml @@ -35,6 +35,11 @@ typedef.dispatch_io_interval_flags_t.skipped = true # Lonely enum constant that we want to tie to dispatch_time_t enum.anonymous.constants.DISPATCH_WALLTIME_NOW.skipped = true +# `dispatch_object_t` is a special union that declares all the different kinds +# of dispatch objects, but that won't work in Rust. +union.dispatch_object_t.skipped = true +typedef.dispatch_object_t.skipped = true + # We want to implement TryFrom on this typedef.dispatch_time_t.skipped = true diff --git a/crates/header-translator/src/config.rs b/crates/header-translator/src/config.rs index e86a5bf7d..5b8e04182 100644 --- a/crates/header-translator/src/config.rs +++ b/crates/header-translator/src/config.rs @@ -220,7 +220,10 @@ pub struct LibraryConfig { pub protocol_data: HashMap, #[serde(rename = "struct")] #[serde(default)] - pub struct_data: HashMap, + pub struct_data: HashMap, + #[serde(rename = "union")] + #[serde(default)] + pub union_data: HashMap, #[serde(rename = "enum")] #[serde(default)] pub enum_data: HashMap, @@ -299,7 +302,7 @@ pub struct ProtocolData { #[derive(Deserialize, Debug, Default, Clone, PartialEq, Eq)] #[serde(deny_unknown_fields)] -pub struct StructData { +pub struct RecordData { #[serde(default)] pub skipped: bool, } diff --git a/crates/header-translator/src/expr.rs b/crates/header-translator/src/expr.rs index ab3cd7f91..a0373686c 100644 --- a/crates/header-translator/src/expr.rs +++ b/crates/header-translator/src/expr.rs @@ -9,7 +9,6 @@ use crate::availability::Availability; use crate::context::MacroLocation; use crate::name_translation::enum_prefix; use crate::rust_type::Ty; -use crate::stmt::new_enum_id; use crate::unexposed_attr::UnexposedAttr; use crate::{immediate_children, Context, ItemIdentifier}; @@ -223,7 +222,7 @@ impl Expr { .get_semantic_parent() .expect("EnumConstantDecl parent"); assert_eq!(parent.get_kind(), EntityKind::EnumDecl); - let parent_id = new_enum_id(&parent, context); + let parent_id = ItemIdentifier::new_optional(&parent, context); let variant = entity.get_name().expect("EnumConstantDecl name"); if parent_id.name.is_some() { let parent_id = parent_id.map_name(|name| name.unwrap()); diff --git a/crates/header-translator/src/global_analysis.rs b/crates/header-translator/src/global_analysis.rs index e401a0668..40d6d39d0 100644 --- a/crates/header-translator/src/global_analysis.rs +++ b/crates/header-translator/src/global_analysis.rs @@ -116,33 +116,36 @@ fn update_module(module: &mut Module, cf_type_id_mapping: &BTreeMap> { pub fn new_optional(entity: &Entity<'_>, context: &Context<'_>) -> Self { - Self::with_name(entity.get_name(), entity, context) + let mut id = Self::with_name(entity.get_name(), entity, context); + + // union (unnamed at /Applications/Xcode.app/...) + // enum (unnamed at /Applications/Xcode.app/...) + if id + .name + .as_deref() + .map(|name| name.contains(" (unnamed at")) + .unwrap_or(false) + { + id.name = None; + } + + id + } + + pub fn to_option(self) -> Option { + if let Some(name) = self.name { + Some(ItemIdentifier::from_raw(name, self.location)) + } else { + None + } } } diff --git a/crates/header-translator/src/rust_type.rs b/crates/header-translator/src/rust_type.rs index 864ab2728..25c20e2c1 100644 --- a/crates/header-translator/src/rust_type.rs +++ b/crates/header-translator/src/rust_type.rs @@ -486,6 +486,11 @@ pub enum Ty { /// Whether the struct's declaration has a bridge attribute. is_bridged: bool, }, + Union { + id: ItemIdentifier>, + /// FIXME: This does not work for recursive structs. + fields: Vec, + }, Fn { is_variadic: bool, no_escape: bool, @@ -621,21 +626,36 @@ impl Ty { TypeKind::Double => Self::Primitive(Primitive::Double), TypeKind::Record => { let declaration = ty.get_declaration().expect("record declaration"); - Self::Struct { - id: ItemIdentifier::new(&declaration, context), - fields: ty - .get_fields() - .expect("struct fields") - .into_iter() - .map(|field| { - Self::parse( - field.get_type().expect("struct field type"), - Lifetime::Unspecified, - context, - ) - }) - .collect(), - is_bridged: is_bridged(&declaration, context), + + let fields = ty + .get_fields() + .expect("struct fields") + .into_iter() + .map(|field| { + Self::parse( + field.get_type().expect("struct field type"), + Lifetime::Unspecified, + context, + ) + }) + .collect(); + + match declaration.get_kind() { + EntityKind::StructDecl => Self::Struct { + id: ItemIdentifier::new(&declaration, context), + fields, + is_bridged: is_bridged(&declaration, context), + }, + EntityKind::UnionDecl => Self::Union { + id: ItemIdentifier::new_optional(&declaration, context), + fields, + }, + _ => { + error!(?declaration, "unknown record type decl"); + Self::GenericParam { + name: "UnknownRecord".into(), + } + } } } TypeKind::Enum => { @@ -1226,6 +1246,16 @@ impl Ty { items.push(id.clone()); items } + Self::Union { id, fields, .. } => { + let mut items = Vec::new(); + for field in fields { + items.extend(field.required_items()); + } + if let Some(id) = id.clone().to_option() { + items.push(id); + } + items + } Self::Fn { is_variadic: _, no_escape: _, @@ -1291,7 +1321,7 @@ impl Ty { element_type.requires_mainthreadmarker(self_requires) } Self::Enum { ty, .. } => ty.requires_mainthreadmarker(self_requires), - Self::Struct { fields, .. } => fields + Self::Struct { fields, .. } | Self::Union { fields, .. } => fields .iter() .any(|field| field.requires_mainthreadmarker(self_requires)), Self::Fn { @@ -1347,6 +1377,9 @@ impl Ty { Self::Struct { fields, .. } => fields .iter() .any(|field| field.provides_mainthreadmarker(self_provides)), + Self::Union { fields, .. } => fields + .iter() + .all(|field| field.provides_mainthreadmarker(self_provides)), _ => false, } } @@ -1445,6 +1478,21 @@ impl Ty { matches!(self, Self::TypeDef { id, .. } if id.name == "CFTypeID") } + pub(crate) fn contains_union(&self) -> bool { + match self { + Self::Union { .. } => true, + Self::TypeDef { id, .. } + if matches!(&*id.name, "MPSPackedFloat3" | "MTLPackedFloat3") => + { + // These are custom-defined to not contain the internal union. + false + } + Self::TypeDef { to, .. } => to.contains_union(), + Self::Struct { fields, .. } => fields.iter().any(|field| field.contains_union()), + _ => false, + } + } + pub(crate) fn is_objc_bool(&self) -> bool { match self { Self::Primitive(Primitive::ObjcBool) => true, @@ -1547,6 +1595,14 @@ impl Ty { Self::Struct { id, .. } => { write!(f, "{}", id.path()) } + Self::Union { id, .. } => { + if let Some(id) = id.clone().to_option() { + write!(f, "{}", id.path()) + } else { + // TODO + write!(f, "UnknownUnion") + } + } Self::Enum { id, .. } => { write!(f, "{}", id.path()) } @@ -2032,7 +2088,7 @@ impl Ty { }) } - pub(crate) fn struct_(&self) -> impl fmt::Display + '_ { + pub(crate) fn record(&self) -> impl fmt::Display + '_ { FormatterFn(move |f| match self { Self::Array { element_type, @@ -2056,7 +2112,7 @@ impl Ty { false } - pub(crate) fn struct_encoding(&self) -> impl fmt::Display + '_ { + pub(crate) fn record_encoding(&self) -> impl fmt::Display + '_ { FormatterFn(move |f| match self { Self::Primitive(Primitive::C99Bool) => write!(f, "Encoding::Bool"), Self::Primitive(Primitive::Long) => write!(f, "Encoding::C_LONG"), @@ -2065,7 +2121,7 @@ impl Ty { Self::TypeDef { to, .. } if to.fn_contains_bool_argument() => { write!(f, "Encoding::Pointer(&Encoding::Unknown)") } - _ => write!(f, "<{}>::ENCODING", self.struct_()), + _ => write!(f, "<{}>::ENCODING", self.record()), }) } @@ -2193,10 +2249,6 @@ impl Ty { Self::parse(ty, Lifetime::Unspecified, context) } - pub(crate) fn is_enum(&self) -> bool { - matches!(self, Self::Enum { .. }) - } - pub(crate) fn pointer_to_opaque_struct_or_void(&self) -> Option> { if let Self::Pointer { pointee, @@ -2273,7 +2325,7 @@ impl Ty { ty } - pub(crate) fn parse_struct_field(ty: Type<'_>, context: &Context<'_>) -> Self { + pub(crate) fn parse_record_field(ty: Type<'_>, context: &Context<'_>) -> Self { Self::parse(ty, Lifetime::Unspecified, context) } @@ -2362,12 +2414,16 @@ impl Ty { matches!(self, Self::Pointer { pointee, .. } if **pointee == Self::Self_) } - pub(crate) fn is_typedef_to(&self, s: &str) -> bool { - matches!(self, Self::TypeDef { id, .. } if id.name == s) + pub(crate) fn is_enum(&self, s: &str) -> bool { + matches!(self, Self::Enum { id, .. } if id.name == s) } - pub(crate) fn is_struct(&self, s: &str) -> bool { - matches!(self, Self::Struct { id, .. } if id.name == s) + pub(crate) fn is_record(&self, s: &str) -> bool { + match self { + Self::Struct { id, .. } if id.name == s => true, + Self::Union { id, .. } if id.name.as_deref() == Some(s) => true, + _ => false, + } } pub(crate) fn is_enum_through_typedef(&self) -> bool { diff --git a/crates/header-translator/src/stmt.rs b/crates/header-translator/src/stmt.rs index e1462f15e..f3d0eed6b 100644 --- a/crates/header-translator/src/stmt.rs +++ b/crates/header-translator/src/stmt.rs @@ -489,7 +489,11 @@ pub enum Stmt { /// typedef struct _name { /// fields* /// } name; - StructDecl { + /// + /// union name { + /// fields* + /// }; + RecordDecl { id: ItemIdentifier, // internal objc struct name (before typedef). shows up in encoding // and is used in message verification. @@ -500,6 +504,7 @@ pub enum Stmt { sendable: Option, packed: bool, documentation: Documentation, + is_union: bool, }, /// typedef NS_OPTIONS(type, name) { /// variants* @@ -622,25 +627,6 @@ fn parse_fn_param_children(parent: &Entity<'_>, context: &Context<'_>) -> Option ret } -pub(crate) fn new_enum_id( - entity: &Entity<'_>, - context: &Context<'_>, -) -> ItemIdentifier> { - assert_eq!(entity.get_kind(), EntityKind::EnumDecl); - let mut id = ItemIdentifier::new_optional(entity, context); - - if id - .name - .as_deref() - .map(|name| name.starts_with("enum (unnamed at")) - .unwrap_or(false) - { - id.name = None; - } - - id -} - impl Stmt { pub fn parse(entity: &Entity<'_>, context: &Context<'_>) -> Vec { let _span = debug_span!( @@ -1131,15 +1117,10 @@ impl Stmt { .expect("typedef underlying type"); let ty = Ty::parse_typedef(ty, context); - // Handled by Stmt::EnumDecl - if ty.is_enum() { - return vec![]; - } - // No need to output a typedef if it'll just point to the same thing. // // TODO: We're discarding a slight bit of availability data this way. - if ty.is_struct(&id.name) { + if ty.is_enum(&id.name) || ty.is_record(&id.name) { return vec![]; } @@ -1214,18 +1195,25 @@ impl Stmt { documentation, }] } - EntityKind::StructDecl => { - let id = ItemIdentifier::new(entity, context); - + EntityKind::StructDecl | EntityKind::UnionDecl => { + let is_union = entity.get_kind() == EntityKind::UnionDecl; + let Some(id) = ItemIdentifier::new_optional(entity, context).to_option() else { + warn!(?entity, "skipped anonymous union"); + return vec![]; + }; let availability = Availability::parse(entity, context); - if context - .library(id.library_name()) - .struct_data - .get(&id.name) - .map(|data| data.skipped) - .unwrap_or_default() - { + let library = context.library(id.library_name()); + let data = if is_union { + &library.union_data + } else { + &library.struct_data + } + .get(&id.name) + .cloned() + .unwrap_or_default(); + + if data.skipped { return vec![]; } @@ -1235,8 +1223,16 @@ impl Stmt { } let ty = entity.get_type().unwrap(); - let enc = ty.get_objc_encoding().unwrap(); - let encoding_name = enc.strip_prefix('{').unwrap().split_once('=').unwrap().0; + let enc = ty.get_objc_encoding().expect("record has encoding"); + let encoding_name = enc + .strip_prefix('{') + .unwrap_or_else(|| { + enc.strip_prefix('(') + .expect("record has { or ( in encoding") + }) + .split_once('=') + .unwrap() + .0; let encoding_name = if encoding_name == id.name { None } else { @@ -1248,26 +1244,28 @@ impl Stmt { let mut sendable = None; let mut packed = false; + let mut res = vec![]; + immediate_children(entity, |entity, span| match entity.get_kind() { EntityKind::UnexposedAttr => { if let Some(attr) = UnexposedAttr::parse(&entity, context) { match attr { UnexposedAttr::Sendable => sendable = Some(true), UnexposedAttr::NonSendable => sendable = Some(false), - attr => error!(?attr, "unknown attribute on struct"), + attr => error!(?attr, "unknown attribute on struct/union"), } } } EntityKind::FieldDecl => { drop(span); - let name = entity.get_name().expect("struct field name"); + let name = entity.get_name().expect("struct/union field name"); let _span = debug_span!("field", name).entered(); - let ty = entity.get_type().expect("struct field type"); - let ty = Ty::parse_struct_field(ty, context); + let ty = entity.get_type().expect("struct/union field type"); + let ty = Ty::parse_record_field(ty, context); if entity.is_bit_field() { - error!("unsound struct bitfield"); + error!("unsound struct/union bitfield"); } let documentation = Documentation::from_entity(&entity); @@ -1276,12 +1274,16 @@ impl Stmt { EntityKind::ObjCBoxable => { boxable = true; } - EntityKind::UnionDecl => error!("can't handle unions in structs yet"), + EntityKind::UnionDecl | EntityKind::StructDecl => { + // Recursively parse inner unions and structs, but + // emit them at the top-level. + res.extend(Self::parse(&entity, context)); + } EntityKind::PackedAttr => packed = true, - kind => error!(?kind, "unknown struct child"), + _ => error!(?entity, "unknown struct/union child"), }); - vec![Self::StructDecl { + res.push(Self::RecordDecl { id, encoding_name, availability, @@ -1290,7 +1292,10 @@ impl Stmt { sendable, packed, documentation: Documentation::from_entity(entity), - }] + is_union, + }); + + res } EntityKind::EnumDecl => { // Enum declarations show up twice for some reason, but @@ -1299,7 +1304,7 @@ impl Stmt { return vec![]; } - let id = new_enum_id(entity, context); + let id = ItemIdentifier::new_optional(entity, context); let data = context .library(id.library_name()) @@ -1672,17 +1677,6 @@ impl Stmt { documentation, }] } - EntityKind::UnionDecl => { - let id = ItemIdentifier::new_optional(entity, context); - warn!( - ?id, - has_attributes = ?entity.has_attributes(), - children = ?entity.get_children(), - documentation = ?entity.get_comment(), - "skipping union", - ); - vec![] - } EntityKind::UnexposedDecl => { // `@compatibility_alias`, can be ignored (since we don't // need to support older SDK versions). @@ -1708,7 +1702,7 @@ impl Stmt { Self::ExternCategory { id, .. } => Some(id.clone()), Self::ProtocolDecl { id, .. } => Some(id.clone()), Self::ProtocolImpl { .. } => None, - Self::StructDecl { id, .. } => Some(id.clone()), + Self::RecordDecl { id, .. } => Some(id.clone()), Self::EnumDecl { id, .. } => Some(id.clone()), Self::ConstDecl { id, .. } => Some(id.clone()), Self::VarDecl { id, .. } => Some(id.clone()), @@ -1728,7 +1722,7 @@ impl Stmt { Self::ExternCategory { id, .. } => id.location(), Self::ProtocolDecl { id, .. } => id.location(), Self::ProtocolImpl { location, .. } => location, - Self::StructDecl { id, .. } => id.location(), + Self::RecordDecl { id, .. } => id.location(), Self::EnumDecl { id, .. } => id.location(), Self::ConstDecl { id, .. } => id.location(), Self::VarDecl { id, .. } => id.location(), @@ -1760,7 +1754,7 @@ impl Stmt { items.extend(protocol_required_items.clone()); items } - Self::StructDecl { fields, .. } => { + Self::RecordDecl { fields, .. } => { let mut items = Vec::new(); for (_, _, field_ty) in fields { items.extend(field_ty.required_items()); @@ -1830,7 +1824,7 @@ impl Stmt { .iter() .flat_map(|method| method.required_items()) .collect(), - Self::StructDecl { .. } => vec![ItemIdentifier::objc("Encoding")], + Self::RecordDecl { .. } => vec![ItemIdentifier::objc("Encoding")], Self::EnumDecl { kind, variants, .. } => { let mut items: Vec<_> = variants .iter() @@ -2368,7 +2362,7 @@ impl Stmt { writeln!(f)?; writeln!(f, ");")?; } - Self::StructDecl { + Self::RecordDecl { id, encoding_name, availability, @@ -2377,6 +2371,7 @@ impl Stmt { sendable, packed, documentation, + is_union, } => { write!(f, "{}", documentation.fmt(Some(id)))?; write!(f, "{}", self.cfg_gate_ln(config))?; @@ -2386,13 +2381,21 @@ impl Stmt { } else { write!(f, "#[repr(C)]")?; } - // HACK to make Bool in structs work. - if fields.iter().any(|(_, _, field)| field.is_objc_bool()) { - write!(f, "#[derive(Clone, Copy, Debug)]")?; + if *is_union || fields.iter().any(|(_, _, field)| field.contains_union()) { + write!(f, "#[derive(Clone, Copy)]")?; + } else { + // HACK to make Bool in structs work. + if fields.iter().any(|(_, _, field)| field.is_objc_bool()) { + write!(f, "#[derive(Clone, Copy, Debug)]")?; + } else { + write!(f, "#[derive(Clone, Copy, Debug, PartialEq)]")?; + } + } + if *is_union { + writeln!(f, "pub union {} {{", id.name)?; } else { - write!(f, "#[derive(Clone, Copy, Debug, PartialEq)]")?; + writeln!(f, "pub struct {} {{", id.name)?; } - writeln!(f, "pub struct {} {{", id.name)?; for (name, documentation, ty) in fields { write!(f, "{}", documentation.fmt(None))?; write!(f, " ")?; @@ -2402,7 +2405,7 @@ impl Stmt { write!(f, "pub ")?; } let name = handle_reserved(name); - writeln!(f, "{name}: {},", ty.struct_())?; + writeln!(f, "{name}: {},", ty.record())?; } writeln!(f, "}}")?; writeln!(f)?; @@ -2413,19 +2416,20 @@ impl Stmt { cfg_gate_ln(required_items, [self.location()], config, self.location()); let encoding = FormatterFn(|f| { - write!( - f, - "Encoding::Struct({:?}, &[", - encoding_name.as_deref().unwrap_or(&id.name), - )?; + if *is_union { + write!(f, "Encoding::Union")?; + } else { + write!(f, "Encoding::Struct")?; + } + write!(f, "({:?}, &[", encoding_name.as_deref().unwrap_or(&id.name),)?; for (_, _, ty) in fields { - write!(f, "{},", ty.struct_encoding())?; + write!(f, "{},", ty.record_encoding())?; } write!(f, "])")?; Ok(()) }); - // SAFETY: The struct is marked `#[repr(C)]`. + // SAFETY: The struct/union is marked `#[repr(C)]`. write!(f, "{cfg_encoding}")?; writeln!(f, "{}", unsafe_impl_encode(&id.name, encoding))?; write!(f, "{cfg_encoding}")?; diff --git a/crates/objc2-encode/src/lib.rs b/crates/objc2-encode/src/lib.rs index 58e11ff03..1e1cd3b6d 100644 --- a/crates/objc2-encode/src/lib.rs +++ b/crates/objc2-encode/src/lib.rs @@ -37,6 +37,7 @@ #![no_std] #![warn(missing_docs)] +#![warn(missing_debug_implementations)] #![warn(clippy::missing_errors_doc)] #![warn(clippy::missing_panics_doc)] // Update in Cargo.toml as well. diff --git a/crates/objc2-proc-macros/src/lib.rs b/crates/objc2-proc-macros/src/lib.rs index ded8d9618..71799e1fd 100644 --- a/crates/objc2-proc-macros/src/lib.rs +++ b/crates/objc2-proc-macros/src/lib.rs @@ -4,6 +4,7 @@ //! exported in other crates. #![warn(missing_docs)] +#![warn(missing_debug_implementations)] #![warn(clippy::missing_errors_doc)] #![warn(clippy::missing_panics_doc)] // Update in Cargo.toml as well. diff --git a/crates/objc2/src/lib.rs b/crates/objc2/src/lib.rs index 776dbbd88..2232402ab 100644 --- a/crates/objc2/src/lib.rs +++ b/crates/objc2/src/lib.rs @@ -162,6 +162,7 @@ #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg, doc_cfg_hide))] #![cfg_attr(docsrs, doc(cfg_hide(doc)))] #![warn(missing_docs)] +#![warn(missing_debug_implementations)] #![warn(clippy::missing_errors_doc)] #![warn(clippy::missing_panics_doc)] // Update in Cargo.toml as well. diff --git a/framework-crates/objc2-audio-toolbox/translation-config.toml b/framework-crates/objc2-audio-toolbox/translation-config.toml index 6800c9450..4995fdba6 100644 --- a/framework-crates/objc2-audio-toolbox/translation-config.toml +++ b/framework-crates/objc2-audio-toolbox/translation-config.toml @@ -15,7 +15,7 @@ typedef.ScheduledAudioSliceCompletionProc.skipped = true struct.ScheduledAudioFileRegion.skipped = true typedef.ScheduledAudioFileRegion.skipped = true typedef.ScheduledAudioFileRegionCompletionProc.skipped = true -# union.AURenderEvent.skipped = true +union.AURenderEvent.skipped = true typedef.AURenderEvent.skipped = true struct.AURenderEventHeader.skipped = true typedef.AURenderEventHeader.skipped = true diff --git a/framework-crates/objc2-core-foundation/src/lib.rs b/framework-crates/objc2-core-foundation/src/lib.rs index f6228e112..ccc8584a7 100644 --- a/framework-crates/objc2-core-foundation/src/lib.rs +++ b/framework-crates/objc2-core-foundation/src/lib.rs @@ -9,8 +9,6 @@ #![cfg_attr(docsrs, feature(doc_auto_cfg))] // Update in Cargo.toml as well. #![doc(html_root_url = "https://docs.rs/objc2-core-foundation/0.2.2")] -// Debug won't be implemented if `CFBase` feature is disabled. -#![allow(missing_debug_implementations)] #[cfg(feature = "alloc")] extern crate alloc; diff --git a/framework-crates/objc2-core-foundation/src/string.rs b/framework-crates/objc2-core-foundation/src/string.rs index eb2688ed2..3ff517bc5 100644 --- a/framework-crates/objc2-core-foundation/src/string.rs +++ b/framework-crates/objc2-core-foundation/src/string.rs @@ -6,8 +6,8 @@ use core::ptr::NonNull; use core::{fmt, str}; use crate::{ - kCFAllocatorNull, Boolean, CFRange, CFRetained, CFString, CFStringCompare, - CFStringCompareFlags, CFStringCreateWithBytes, CFStringCreateWithBytesNoCopy, CFStringEncoding, + kCFAllocatorNull, Boolean, CFRange, CFRetained, CFString, CFStringBuiltInEncodings, + CFStringCompare, CFStringCompareFlags, CFStringCreateWithBytes, CFStringCreateWithBytesNoCopy, CFStringGetBytes, CFStringGetCStringPtr, CFStringGetLength, }; @@ -37,7 +37,7 @@ impl CFString { None, string.as_ptr(), len, - CFStringEncoding::UTF8, + CFStringBuiltInEncodings::EncodingUTF8.0, false as Boolean, ) }; @@ -63,7 +63,7 @@ impl CFString { None, string.as_ptr(), len, - CFStringEncoding::UTF8, + CFStringBuiltInEncodings::EncodingUTF8.0, false as Boolean, kCFAllocatorNull, ) @@ -92,7 +92,8 @@ impl CFString { // encoded strings, see the `as_str_broken` test below. #[allow(dead_code)] unsafe fn as_str_unchecked(&self) -> Option<&str> { - let bytes = unsafe { CFStringGetCStringPtr(self, CFStringEncoding::UTF8) }; + let bytes = + unsafe { CFStringGetCStringPtr(self, CFStringBuiltInEncodings::EncodingUTF8.0) }; NonNull::new(bytes as *mut c_char).map(|bytes| { // SAFETY: The pointer is valid for as long as the CFString is not // mutated (which the caller ensures it isn't for the lifetime of @@ -139,7 +140,7 @@ impl fmt::Display for CFString { location: location_utf16, length: len_utf16 - location_utf16, }, - CFStringEncoding::UTF8, + CFStringBuiltInEncodings::EncodingUTF8.0, 0, // No conversion character false as Boolean, buf.as_mut_ptr(), @@ -212,15 +213,23 @@ mod tests { let table = [ ( b"abc\xf8xyz\0" as &[u8], - CFStringEncoding::ISOLatin1, + CFStringBuiltInEncodings::EncodingISOLatin1, "abcøxyz", ), - (b"\x26\x65\0", CFStringEncoding::UTF16BE, "♥"), - (b"\x65\x26\0", CFStringEncoding::UTF16LE, "♥"), + ( + b"\x26\x65\0", + CFStringBuiltInEncodings::EncodingUTF16BE, + "♥", + ), + ( + b"\x65\x26\0", + CFStringBuiltInEncodings::EncodingUTF16LE, + "♥", + ), ]; for (cstr, encoding, expected) in table { let cstr = CStr::from_bytes_with_nul(cstr).unwrap(); - let s = unsafe { CFStringCreateWithCString(None, cstr.as_ptr(), encoding) }.unwrap(); + let s = unsafe { CFStringCreateWithCString(None, cstr.as_ptr(), encoding.0) }.unwrap(); assert_eq!(s.to_string(), expected); } } @@ -232,7 +241,7 @@ mod tests { None, b"\xd8\x3d\xde".as_ptr(), 3, - CFStringEncoding::UTF16BE, + CFStringBuiltInEncodings::EncodingUTF16BE.0, 0, ) .unwrap() @@ -255,7 +264,7 @@ mod tests { &s, buf.as_mut_ptr().cast(), buf.len() as _, - CFStringEncoding::UTF8, + CFStringBuiltInEncodings::EncodingUTF8.0, ) }, false as _, @@ -288,7 +297,7 @@ mod tests { CFStringCreateWithCString( None, b"\x65\x26\0".as_ptr().cast(), - CFStringEncoding::Unicode, + CFStringBuiltInEncodings::EncodingUnicode.0, ) } .unwrap(); @@ -304,7 +313,7 @@ mod tests { &s, buf.as_mut_ptr().cast(), buf.len() as _, - CFStringEncoding::UTF8, + CFStringBuiltInEncodings::EncodingUTF8.0, ) }, false as _, diff --git a/framework-crates/objc2-core-text/src/lib.rs b/framework-crates/objc2-core-text/src/lib.rs index ef221ad88..58a096831 100644 --- a/framework-crates/objc2-core-text/src/lib.rs +++ b/framework-crates/objc2-core-text/src/lib.rs @@ -16,13 +16,9 @@ extern crate alloc; extern crate std; mod generated; -#[cfg(feature = "SFNTLayoutTypes")] -mod sfnt_lookup_header; #[allow(unused_imports, unreachable_pub)] pub use self::generated::*; -#[cfg(feature = "SFNTLayoutTypes")] -pub use self::sfnt_lookup_header::SFNTLookupFormatSpecificHeader; #[allow(dead_code)] pub(crate) type Fixed = i32; diff --git a/framework-crates/objc2-core-text/src/sfnt_lookup_header.rs b/framework-crates/objc2-core-text/src/sfnt_lookup_header.rs deleted file mode 100644 index 64388f983..000000000 --- a/framework-crates/objc2-core-text/src/sfnt_lookup_header.rs +++ /dev/null @@ -1,48 +0,0 @@ -use core::{fmt, ptr}; - -#[cfg(feature = "objc2")] -use objc2::encode::{Encode, Encoding}; - -use crate::{ - SFNTLookupArrayHeader, SFNTLookupSegmentHeader, SFNTLookupSingleHeader, - SFNTLookupTrimmedArrayHeader, SFNTLookupVectorHeader, -}; - -#[repr(C)] -#[derive(Clone, Copy)] -pub union SFNTLookupFormatSpecificHeader { - the_array: SFNTLookupArrayHeader, - segment: SFNTLookupSegmentHeader, - single: SFNTLookupSingleHeader, - trimmed_array: SFNTLookupTrimmedArrayHeader, - vector: SFNTLookupVectorHeader, -} - -impl fmt::Debug for SFNTLookupFormatSpecificHeader { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("SFNTLookupFormatSpecificHeader") - .finish_non_exhaustive() - } -} - -impl PartialEq for SFNTLookupFormatSpecificHeader { - fn eq(&self, other: &Self) -> bool { - // This is probably wrong, but it's difficult to - // implement a more correct comparison. - ptr::eq(self, other) - } -} - -#[cfg(feature = "objc2")] -unsafe impl Encode for SFNTLookupFormatSpecificHeader { - const ENCODING: Encoding = Encoding::Union( - "SFNTLookupFormatSpecificHeader", - &[ - ::ENCODING, - ::ENCODING, - ::ENCODING, - ::ENCODING, - ::ENCODING, - ], - ); -} diff --git a/framework-crates/objc2-core-text/translation-config.toml b/framework-crates/objc2-core-text/translation-config.toml index dce55a4c1..ca9324a29 100644 --- a/framework-crates/objc2-core-text/translation-config.toml +++ b/framework-crates/objc2-core-text/translation-config.toml @@ -9,25 +9,6 @@ tvos = "9.0" watchos = "2.0" visionos = "1.0" -# Typedef'd to a union -typedef.SFNTLookupFormatSpecificHeader.skipped = true # Manually defined -typedef.MortSpecificSubtable.skipped = true -typedef.MorxSpecificSubtable.skipped = true -typedef.KernFormatSpecificHeader.skipped = true -typedef.KerxFormatSpecificHeader.skipped = true -typedef.BslnFormatUnion.skipped = true - -# Uses the above unions -struct.MortSubtable.skipped = true -struct.MorxSubtable.skipped = true -struct.KernVersion0SubtableHeader.skipped = true -struct.KernSubtableHeader.skipped = true -typedef.KernSubtableHeaderPtr.skipped = true -struct.KerxSubtableHeader.skipped = true -typedef.KerxSubtableHeaderPtr.skipped = true -struct.BslnTable.skipped = true -typedef.BslnTablePtr.skipped = true - # Unknown calling convention / ABI fn.CTRubyAnnotationCreate.skipped = true typedef.BslnBaselineRecord.skipped = true diff --git a/framework-crates/objc2-metal-performance-shaders/translation-config.toml b/framework-crates/objc2-metal-performance-shaders/translation-config.toml index 7b6ec0c62..678b0d216 100644 --- a/framework-crates/objc2-metal-performance-shaders/translation-config.toml +++ b/framework-crates/objc2-metal-performance-shaders/translation-config.toml @@ -49,7 +49,7 @@ typedef.MPSIntersectionDistancePrimitiveIndexBufferIndexInstanceIndexCoordinates # tries to derive with `PartialEq` which fails because of `Bool` fields # struct.MPSImageHistogramInfo.skipped = true -# Uses unions internally +# Has a union field, but we'd rather have it as just a struct struct._MPSPackedFloat3.skipped = true typedef.MPSPackedFloat3.skipped = true diff --git a/framework-crates/objc2-metal/translation-config.toml b/framework-crates/objc2-metal/translation-config.toml index a0cb12515..9176db0d7 100644 --- a/framework-crates/objc2-metal/translation-config.toml +++ b/framework-crates/objc2-metal/translation-config.toml @@ -34,7 +34,7 @@ protocol.MTLDevice.methods."newLibraryWithData:error:".skipped = true # Needs mach / kernel types protocol.MTLResource.methods."setOwnerWithIdentity:".skipped = true -# Uses unions internally +# Has a union field, but we'd rather have it as just a struct struct._MTLPackedFloat3.skipped = true typedef.MTLPackedFloat3.skipped = true fn.MTLPackedFloat3Make.skipped = true diff --git a/generated b/generated index 30e7b9fd5..eb674b3c2 160000 --- a/generated +++ b/generated @@ -1 +1 @@ -Subproject commit 30e7b9fd514026f5aa2ed33edb1c6ad8268dfba6 +Subproject commit eb674b3c25acc672ca8f6f07d36cdad351b34367