From 219835e97c96bfd8f6c9639226431700aab2d0c6 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Wed, 7 Feb 2024 13:58:38 +0100 Subject: [PATCH 1/4] Parse expressions more thoroughly --- crates/header-translator/src/expr.rs | 252 ++++++++++++------ crates/header-translator/src/stmt.rs | 81 +++--- crates/icrate/CHANGELOG.md | 2 + crates/icrate/src/additions/Foundation/mod.rs | 2 - .../src/additions/Foundation/not_found.rs | 7 - crates/icrate/src/generated | 2 +- 6 files changed, 220 insertions(+), 126 deletions(-) delete mode 100644 crates/icrate/src/additions/Foundation/not_found.rs diff --git a/crates/header-translator/src/expr.rs b/crates/header-translator/src/expr.rs index f908e38c5..475476430 100644 --- a/crates/header-translator/src/expr.rs +++ b/crates/header-translator/src/expr.rs @@ -1,95 +1,136 @@ +use std::collections::BTreeMap; use std::fmt; -use std::fmt::Write; use clang::token::TokenKind; -use clang::{Entity, EntityKind, EntityVisitResult}; +use clang::{Entity, EntityKind, EntityVisitResult, EvaluationResult}; -use crate::context::Context; -use crate::immediate_children; -use crate::unexposed_attr::UnexposedAttr; +use crate::rust_type::Ty; +use crate::stmt::new_enum_id; +use crate::{Context, ItemIdentifier}; + +#[derive(Clone, Debug, PartialEq)] +pub enum Token { + Punctuation(String), + Literal(String), + Expr(Expr), +} #[derive(Clone, Debug, PartialEq)] pub enum Expr { - NSUIntegerMax, - NSIntegerMax, Signed(i64), Unsigned(u64), - String(String), + Float(f64), + MacroInvocation { + name: String, + evaluated: Option>, + }, + Enum { + id: ItemIdentifier, + variant: String, + }, + Const(ItemIdentifier), + Var { + id: ItemIdentifier, + ty: Ty, + }, + Tokens(Vec), } impl Expr { - pub fn from_val((signed, unsigned): (i64, u64), is_signed: bool, pointer_width: usize) -> Self { - let (signed_max, unsigned_max) = match pointer_width { - 64 => (i64::MAX, u64::MAX), - 32 => (i32::MAX as i64, u32::MAX as u64), - 16 => (i16::MAX as i64, u16::MAX as u64), - pw => panic!("unhandled pointer width {pw}"), - }; - - if unsigned == unsigned_max { - Expr::NSUIntegerMax - } else if signed == signed_max { - Expr::NSIntegerMax - } else if is_signed { - Expr::Signed(signed) - } else { - Expr::Unsigned(unsigned) + fn from_evaluated(entity: &Entity<'_>) -> Self { + let res = entity + .evaluate() + .expect("must be able to evaluate result of macro in expression"); + match res { + EvaluationResult::SignedInteger(n) => Expr::Signed(n), + EvaluationResult::UnsignedInteger(n) => Expr::Unsigned(n), + EvaluationResult::Float(n) => Self::Float(n), + res => panic!("unexpected evaluation result {res:?}"), } } - pub fn parse_enum_constant(entity: &Entity<'_>, context: &Context<'_>) -> Option { - let mut declaration_references = Vec::new(); + pub fn parse_enum_constant(entity: &Entity<'_>, context: &Context<'_>) -> Self { + Self::parse(entity, context) + } - entity.visit_children(|entity, _parent| { - if let EntityKind::DeclRefExpr = entity.get_kind() { - let name = entity.get_name().expect("expr decl ref name"); - declaration_references.push(name); + pub fn parse_var(entity: &Entity<'_>, context: &Context<'_>) -> Self { + Self::parse(entity, context) + } + + fn parse(entity: &Entity<'_>, context: &Context<'_>) -> Self { + match (entity.get_kind(), &*entity.get_children()) { + (EntityKind::ParenExpr, [_]) => { + // TODO: Remove unnecessary top-level parentheses + // Self::parse(child, context) + Self::parse_from_tokens(entity, context) } - EntityVisitResult::Recurse - }); + (EntityKind::DeclRefExpr, []) => Self::parse_from_decl_ref(entity, context), + // We can't really use the information in here for much, since the + // kind of operator is not exposed. So we fall back to parsing raw + // tokens instead. + (EntityKind::UnaryOperator, [_]) => Self::parse_from_tokens(entity, context), + (EntityKind::BinaryOperator, [_, _]) => Self::parse_from_tokens(entity, context), + (EntityKind::IntegerLiteral, []) => Self::parse_from_tokens(entity, context), + (EntityKind::FloatingLiteral, []) => Self::parse_from_tokens(entity, context), + // Remove unnecessary cast + (EntityKind::CStyleCastExpr, [_type, child]) => Self::parse(child, context), + (EntityKind::UnexposedExpr, _) => Self::parse_from_tokens(entity, context), + (_, children) => panic!("unknown expr: {entity:?}, {children:#?}"), + } + } - let mut res = None; + fn parse_from_tokens(entity: &Entity<'_>, context: &Context<'_>) -> Self { + let mut declaration_references = BTreeMap::new(); - immediate_children(entity, |entity, _span| match entity.get_kind() { - EntityKind::UnexposedAttr => { - if let Some(attr) = UnexposedAttr::parse(&entity, context) { - error!(?attr, "unknown attribute"); - } - } - _ => { - if res.is_none() { - res = Self::parse(&entity, &declaration_references); - } else { - panic!("found multiple expressions where one was expected"); - } + entity.visit_children(|entity, _parent| { + if let EntityKind::DeclRefExpr = entity.get_kind() { + let name = entity.get_name().expect("DeclRefExpr name"); + declaration_references.insert(name, Self::parse_from_decl_ref(&entity, context)); } + EntityVisitResult::Recurse }); - res - } - - pub fn parse_var(entity: &Entity<'_>) -> Option { - Self::parse(entity, &[]) - } - - fn parse(entity: &Entity<'_>, declaration_references: &[String]) -> Option { let range = entity.get_range().expect("expr range"); let tokens = range.tokenize(); if tokens.is_empty() { - // TODO: Find a better way to parse macros - return None; + let location = entity.get_location().expect("expr location"); + if let Some(macro_invocation) = context + .macro_invocations + .get(&location.get_spelling_location()) + { + let name = macro_invocation + .get_name() + .expect("expr macro invocation name"); + return Expr::MacroInvocation { + name, + evaluated: Some(Box::new(Self::from_evaluated(entity))), + }; + } else { + return Self::from_evaluated(entity); + } } - let mut s = String::new(); + let mut res = vec![]; for token in &tokens { - match (token.get_kind(), token.get_spelling()) { + res.push(match (token.get_kind(), token.get_spelling()) { (TokenKind::Identifier, ident) => { - if declaration_references.contains(&ident) { - // TODO: Handle these specially when we need to - } - write!(s, "{ident}").unwrap(); + Token::Expr(if let Some(expr) = declaration_references.get(&ident) { + expr.clone() + } else { + let macro_invocation = context + .macro_invocations + .get(&token.get_location().get_spelling_location()) + .expect("expr macro invocation"); + let name = macro_invocation + .get_name() + .expect("expr macro invocation name"); + Expr::MacroInvocation { + name, + evaluated: None, + } + }) } (TokenKind::Literal, lit) => { let lit = lit @@ -98,52 +139,95 @@ impl Expr { .trim_end_matches('u') .trim_end_matches('U'); let lit = lit.replace("0X", "0x"); - write!(s, "{lit}").unwrap(); + Token::Literal(lit) } (TokenKind::Punctuation, punct) => { match &*punct { // These have the same semantics in C and Rust - "(" | ")" | "<<" | "-" | "+" | "|" | "&" | "^" => { - write!(s, "{punct}").unwrap() - } + "(" | ")" | "<<" | "-" | "+" | "|" | "&" | "^" => Token::Punctuation(punct), // Bitwise not - "~" => write!(s, "!").unwrap(), + "~" => Token::Punctuation("!".to_string()), punct => panic!("unknown expr punctuation {punct}"), } } (kind, spelling) => panic!("unknown expr token {kind:?}/{spelling}"), - } + }); } - // Trim casts - s = s - .trim_start_matches("(NSBoxType)") - .trim_start_matches("(NSBezelStyle)") - .trim_start_matches("(NSEventSubtype)") - .trim_start_matches("(NSWindowButton)") - .trim_start_matches("(NSExpressionType)") - .to_string(); - // Trim unnecessary parentheses - if s.starts_with('(') - && s.ends_with(')') - && s.chars().filter(|&c| c == '(' || c == ')').count() == 2 + if res.first() == Some(&Token::Punctuation("(".to_string())) + && res.last() == Some(&Token::Punctuation(")".to_string())) { - s = s.trim_start_matches('(').trim_end_matches(')').to_string(); + res.remove(0); + res.pop(); } - Some(Self::String(s)) + Self::Tokens(res) + } + + fn parse_from_decl_ref(entity: &Entity<'_>, context: &Context<'_>) -> Self { + assert_eq!(entity.get_kind(), EntityKind::DeclRefExpr); + let definition = entity.get_definition().expect("DeclRefExpr definition"); + assert_eq!(entity.get_name(), definition.get_name()); + match definition.get_kind() { + EntityKind::EnumConstantDecl => { + let parent = definition + .get_semantic_parent() + .expect("EnumConstantDecl parent"); + let parent_id = new_enum_id(&parent, context); + let name = entity.get_name().expect("EnumConstantDecl name"); + if parent_id.name.is_some() { + Self::Enum { + id: parent_id.map_name(|name| name.unwrap()), + variant: name, + } + } else { + Self::Const(parent_id.map_name(|_| name)) + } + } + EntityKind::VarDecl => Self::Var { + id: ItemIdentifier::new(&definition, context), + ty: Ty::parse_static(definition.get_type().expect("var type"), context), + }, + _ => panic!("unknown DeclRefExpr {definition:#?} in {entity:#?}"), + } } } impl fmt::Display for Expr { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - Self::NSUIntegerMax => write!(f, "NSUIntegerMax as _"), - Self::NSIntegerMax => write!(f, "NSIntegerMax as _"), Self::Signed(signed) => write!(f, "{signed}"), Self::Unsigned(unsigned) => write!(f, "{unsigned}"), - Self::String(s) => write!(f, "{s}"), + Self::Float(n) => write!(f, "{n}"), + Self::MacroInvocation { name, evaluated } => { + if name == "NSIntegerMax" { + write!(f, "NSIntegerMax as _") + } else if name == "NSUIntegerMax" { + write!(f, "NSUIntegerMax as _") + } else if name == "FLT_MAX" { + write!(f, "c_float::MAX as _") + } else if name == "DBL_MAX" { + write!(f, "c_double::MAX as _") + } else if let Some(evaluated) = evaluated { + write!(f, "{evaluated}") + } else { + write!(f, "{name}") + } + } + Self::Enum { id: _, variant } => write!(f, "{variant}"), + Self::Const(id) => write!(f, "{}", id.name), + Self::Var { id, ty: _ } => write!(f, "{}", id.name), + Self::Tokens(tokens) => { + for token in tokens { + match token { + Token::Punctuation(punct) => write!(f, "{punct}")?, + Token::Literal(lit) => write!(f, "{lit}")?, + Token::Expr(expr) => write!(f, "{expr}")?, + } + } + Ok(()) + } } } } diff --git a/crates/header-translator/src/stmt.rs b/crates/header-translator/src/stmt.rs index 6b12746d9..a29c7126b 100644 --- a/crates/header-translator/src/stmt.rs +++ b/crates/header-translator/src/stmt.rs @@ -509,6 +509,25 @@ fn get_class_implied_features(cls: &Entity<'_>, context: &Context<'_>) -> Vec, + 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!( @@ -1077,16 +1096,7 @@ impl Stmt { return vec![]; } - 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; - } + let id = new_enum_id(entity, context); let data = context .enum_data @@ -1120,21 +1130,37 @@ impl Stmt { return; } - let pointer_width = - entity.get_translation_unit().get_target().pointer_width; + let value = entity + .get_enum_constant_value() + .expect("enum constant value"); - let val = Expr::from_val( - entity - .get_enum_constant_value() - .expect("enum constant value"), - is_signed, - pointer_width, - ); - let expr = if data.use_value { - val + let mut expr = if is_signed { + Expr::Signed(value.0) } else { - Expr::parse_enum_constant(&entity, context).unwrap_or(val) + Expr::Unsigned(value.1) }; + + if !data.use_value { + // Some enums constants don't declare a value, but + // let it be inferred from the position in the + // enum instead; in those cases, we use the value + // generated above. + immediate_children(&entity, |entity, _span| match entity.get_kind() { + EntityKind::UnexposedAttr => { + if let Some(attr) = UnexposedAttr::parse(&entity, context) { + error!(?attr, "unknown attribute"); + } + } + EntityKind::VisibilityAttr => {} + _ if entity.is_expression() => { + expr = Expr::parse_enum_constant(&entity, context); + } + _ => { + panic!("unknown EnumConstantDecl child in {name:?}: {entity:?}") + } + }); + }; + variants.push((name, availability, expr)); } EntityKind::UnexposedAttr => { @@ -1210,7 +1236,7 @@ impl Stmt { EntityKind::TypeRef => {} _ if entity.is_expression() => { if value.is_none() { - value = Some(Expr::parse_var(&entity)); + value = Some(Expr::parse_var(&entity, context)); } else { panic!("got variable value twice") } @@ -1218,15 +1244,6 @@ impl Stmt { _ => panic!("unknown vardecl child in {id:?}: {entity:?}"), }); - let value = match value { - Some(Some(expr)) => Some(expr), - Some(None) => { - warn!("skipped static"); - return vec![]; - } - None => None, - }; - vec![Self::VarDecl { id, availability, diff --git a/crates/icrate/CHANGELOG.md b/crates/icrate/CHANGELOG.md index fafaffa46..0004f47c9 100644 --- a/crates/icrate/CHANGELOG.md +++ b/crates/icrate/CHANGELOG.md @@ -13,6 +13,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ### Added * Added `NSObject` categories, notably those used by key-value coding and observing. +* Added a few statics that were previously omitted (notably a few + `NSWindowLevel` constants). ### Changed * Updated SDK from Xcode 15.0.1 to 15.2. diff --git a/crates/icrate/src/additions/Foundation/mod.rs b/crates/icrate/src/additions/Foundation/mod.rs index 9fac3a7d5..666d76005 100644 --- a/crates/icrate/src/additions/Foundation/mod.rs +++ b/crates/icrate/src/additions/Foundation/mod.rs @@ -41,7 +41,6 @@ mod fast_enumeration_state; mod generics; mod geometry; mod macros; -mod not_found; mod ns_consumed; mod number; mod process_info; @@ -67,7 +66,6 @@ pub use self::geometry::{ CGFloat, CGPoint, CGRect, CGSize, NSMaxXEdge, NSMaxYEdge, NSMinXEdge, NSMinYEdge, NSPoint, NSRect, NSRectEdge, NSRectEdgeMaxX, NSRectEdgeMaxY, NSRectEdgeMinX, NSRectEdgeMinY, NSSize, }; -pub use self::not_found::NSNotFound; #[cfg(feature = "Foundation_NSMapTable")] pub use self::ns_consumed::NSFreeMapTable; pub use self::range::NSRange; diff --git a/crates/icrate/src/additions/Foundation/not_found.rs b/crates/icrate/src/additions/Foundation/not_found.rs deleted file mode 100644 index 54207d2dd..000000000 --- a/crates/icrate/src/additions/Foundation/not_found.rs +++ /dev/null @@ -1,7 +0,0 @@ -use objc2::ffi::{NSInteger, NSIntegerMax}; - -/// A value indicating that a requested item couldn’t be found or doesn’t exist. -/// -/// See [Apple's documentation](https://developer.apple.com/documentation/foundation/nsnotfound?language=objc). -#[allow(non_upper_case_globals)] -pub const NSNotFound: NSInteger = NSIntegerMax; diff --git a/crates/icrate/src/generated b/crates/icrate/src/generated index ef1c8aee7..6923ff9e6 160000 --- a/crates/icrate/src/generated +++ b/crates/icrate/src/generated @@ -1 +1 @@ -Subproject commit ef1c8aee7e61a5cf5ec5a42c53076679b1efb275 +Subproject commit 6923ff9e616327edde00a93dcf1e4a1edc4a491c From 5d3fd2d9aa0d5087334dfd932926b070af5c920d Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Wed, 7 Feb 2024 14:10:43 +0100 Subject: [PATCH 2/4] Refactor how anonymous enums are emitted --- crates/header-translator/src/cache.rs | 2 +- crates/header-translator/src/stmt.rs | 88 ++++++++++++++----- .../header-translator/src/unexposed_attr.rs | 1 + crates/icrate/src/generated | 2 +- crates/icrate/src/macros.rs | 19 ---- .../test-ui/ui/msg_send_invalid_error.stderr | 2 +- crates/test-ui/ui/nsarray_not_message.stderr | 4 +- 7 files changed, 70 insertions(+), 48 deletions(-) diff --git a/crates/header-translator/src/cache.rs b/crates/header-translator/src/cache.rs index b13f74cc0..b5329de73 100644 --- a/crates/header-translator/src/cache.rs +++ b/crates/header-translator/src/cache.rs @@ -199,7 +199,7 @@ impl<'a> Cache<'a> { }) = iter.peek_mut() { if enum_ty.is_typedef_to(&id.name) { - *enum_id = id.clone().to_some(); + *enum_id = id.clone(); *enum_ty = ty.clone(); // Skip adding the now-redundant alias to the list of statements continue; diff --git a/crates/header-translator/src/stmt.rs b/crates/header-translator/src/stmt.rs index a29c7126b..4f732695b 100644 --- a/crates/header-translator/src/stmt.rs +++ b/crates/header-translator/src/stmt.rs @@ -443,13 +443,26 @@ pub enum Stmt { /// variants* /// }; EnumDecl { - id: ItemIdentifier>, + id: ItemIdentifier, availability: Availability, ty: Ty, kind: Option, variants: Vec<(String, Availability, Expr)>, sendable: Option, }, + /// Anonymous enum variants are emitted as free constants. + /// + /// enum { + /// variants* + /// }; + ConstDecl { + id: ItemIdentifier, + availability: Availability, + ty: Ty, + value: Expr, + // Hack to get prettier output + is_last: bool, + }, /// static const ty name = expr; /// extern const ty name; VarDecl { @@ -1195,18 +1208,37 @@ impl Stmt { _ => error!("unknown"), }); - if id.name.is_none() && variants.is_empty() { - return vec![]; + if id.name.is_none() { + // Availability propagates to the variants automatically + let _ = availability; + // TODO: Unsure how to handle error enums + assert!(matches!( + kind, + None | Some(UnexposedAttr::Enum) | Some(UnexposedAttr::ErrorEnum) + )); + assert_eq!(sendable, None); + let variants_len = variants.len(); + variants + .into_iter() + .enumerate() + .map(|(i, (name, availability, value))| Self::ConstDecl { + id: id.clone().map_name(|_| name), + availability, + ty: ty.clone(), + value, + is_last: i == variants_len - 1, + }) + .collect() + } else { + vec![Self::EnumDecl { + id: id.map_name(|name| name.unwrap()), + availability, + ty, + kind, + variants, + sendable, + }] } - - vec![Self::EnumDecl { - id, - availability, - ty, - kind, - variants, - sendable, - }] } EntityKind::VarDecl => { let id = ItemIdentifier::new(entity, context); @@ -1395,7 +1427,8 @@ impl Stmt { Stmt::ProtocolDecl { id, .. } => Some(&id.name), Stmt::ProtocolImpl { .. } => None, Stmt::StructDecl { id, .. } => Some(&id.name), - Stmt::EnumDecl { id, .. } => id.name.as_deref(), + Stmt::EnumDecl { id, .. } => Some(&id.name), + Stmt::ConstDecl { id, .. } => Some(&id.name), Stmt::VarDecl { id, .. } => Some(&id.name), Stmt::FnDecl { id, body, .. } if body.is_none() => Some(&*id.name), // TODO @@ -1852,11 +1885,7 @@ impl fmt::Display for Stmt { writeln!(f, "{macro_name}!(")?; writeln!(f, " #[underlying({ty})]")?; write!(f, "{availability}")?; - writeln!( - f, - " pub enum {} {{", - id.name.as_deref().unwrap_or("__anonymous__") - )?; + writeln!(f, " pub enum {} {{", id.name)?; for (name, availability, expr) in variants { write!(f, "{availability}")?; writeln!(f, " {name} = {expr},")?; @@ -1865,13 +1894,24 @@ impl fmt::Display for Stmt { writeln!(f, ");")?; if let Some(true) = sendable { - if let Some(name) = &id.name { - writeln!(f)?; - writeln!(f, "unsafe impl Send for {name} {{}}")?; + writeln!(f)?; + writeln!(f, "unsafe impl Send for {} {{}}", id.name)?; - writeln!(f)?; - writeln!(f, "unsafe impl Sync for {name} {{}}")?; - } + writeln!(f)?; + writeln!(f, "unsafe impl Sync for {} {{}}", id.name)?; + } + } + Self::ConstDecl { + id, + availability, + ty, + value, + is_last, + } => { + write!(f, "{availability}")?; + write!(f, "pub const {}: {ty} = {value};", id.name)?; + if *is_last { + writeln!(f)?; } } Self::VarDecl { diff --git a/crates/header-translator/src/unexposed_attr.rs b/crates/header-translator/src/unexposed_attr.rs index 24f4f5327..171be9861 100644 --- a/crates/header-translator/src/unexposed_attr.rs +++ b/crates/header-translator/src/unexposed_attr.rs @@ -49,6 +49,7 @@ impl UnexposedAttr { } "NS_ERROR_ENUM" => { let _ = get_arguments(); + // TODO: Add error domain here Some(Self::ErrorEnum) } "NS_TYPED_ENUM" | "NS_STRING_ENUM" | "CF_TYPED_ENUM" => Some(Self::TypedEnum), diff --git a/crates/icrate/src/generated b/crates/icrate/src/generated index 6923ff9e6..9074b1549 160000 --- a/crates/icrate/src/generated +++ b/crates/icrate/src/generated @@ -1 +1 @@ -Subproject commit 6923ff9e616327edde00a93dcf1e4a1edc4a491c +Subproject commit 9074b15495d2ef82db10346161ebcfeec990b380 diff --git a/crates/icrate/src/macros.rs b/crates/icrate/src/macros.rs index c239ec115..bb796551f 100644 --- a/crates/icrate/src/macros.rs +++ b/crates/icrate/src/macros.rs @@ -60,25 +60,6 @@ macro_rules! extern_struct { } macro_rules! extern_enum { - ( - #[underlying($ty:ty)] - $(#[$m:meta])* - $v:vis enum __anonymous__ { - $( - $(#[$field_m:meta])* - $field:ident = $value:expr - ),* $(,)? - } - ) => { - extern_enum_inner! { - ($v) - ($ty) - $( - $(#[$field_m])* - $field = $value, - )* - } - }; ( #[underlying($ty:ty)] $(#[$m:meta])* diff --git a/crates/test-ui/ui/msg_send_invalid_error.stderr b/crates/test-ui/ui/msg_send_invalid_error.stderr index 79c6b7c2b..56969b188 100644 --- a/crates/test-ui/ui/msg_send_invalid_error.stderr +++ b/crates/test-ui/ui/msg_send_invalid_error.stderr @@ -47,7 +47,7 @@ error[E0277]: the trait bound `i32: Message` is not satisfied AnyObject NSArray NSMutableArray - NSDictionary + __RcTestObject and $N others note: required by a bound in `send_message_error` --> $WORKSPACE/crates/objc2/src/__macro_helpers/msg_send.rs diff --git a/crates/test-ui/ui/nsarray_not_message.stderr b/crates/test-ui/ui/nsarray_not_message.stderr index 432af87e0..80843a8f3 100644 --- a/crates/test-ui/ui/nsarray_not_message.stderr +++ b/crates/test-ui/ui/nsarray_not_message.stderr @@ -12,7 +12,7 @@ error[E0277]: the trait bound `i32: Message` is not satisfied AnyObject NSArray NSMutableArray - NSDictionary + __RcTestObject and $N others note: required by a bound in `icrate::generated::Foundation::__NSArray::>::new` --> $WORKSPACE/crates/icrate/src/generated/Foundation/NSArray.rs @@ -42,9 +42,9 @@ error[E0277]: the trait bound `Id: ClassType` is not satisfied __NSProxy NSArray NSMutableArray + __RcTestObject NSDictionary NSMutableDictionary - __RcTestObject NSSet and $N others = note: required for `Id` to implement `IsRetainable` From 0c9ba96ecba57f71730808146f25216be9ccb7ac Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Wed, 7 Feb 2024 09:53:06 +0100 Subject: [PATCH 3/4] Check primitive-ness of enum type --- crates/header-translator/src/rust_type.rs | 237 ++++++++++++---------- 1 file changed, 131 insertions(+), 106 deletions(-) diff --git a/crates/header-translator/src/rust_type.rs b/crates/header-translator/src/rust_type.rs index a5753c859..6b4133888 100644 --- a/crates/header-translator/src/rust_type.rs +++ b/crates/header-translator/src/rust_type.rs @@ -449,8 +449,7 @@ fn check_nullability(ty: &Type<'_>, new: Option) -> Nullability { } #[derive(Debug, Clone, PartialEq, Eq, Hash)] -enum Inner { - // Primitives +enum Primitive { Void, C99Bool, Char, @@ -478,6 +477,55 @@ enum Inner { U64, ISize, USize, + // Objective-C + ObjcBool, + NSInteger, + NSUInteger, +} + +impl fmt::Display for Primitive { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + // Primitives + Self::Void => write!(f, "c_void"), + Self::C99Bool => write!(f, "bool"), + Self::Char => write!(f, "c_char"), + Self::SChar => write!(f, "c_schar"), + Self::UChar => write!(f, "c_uchar"), + Self::Short => write!(f, "c_short"), + Self::UShort => write!(f, "c_ushort"), + Self::Int => write!(f, "c_int"), + Self::UInt => write!(f, "c_uint"), + Self::Long => write!(f, "c_long"), + Self::ULong => write!(f, "c_ulong"), + Self::LongLong => write!(f, "c_longlong"), + Self::ULongLong => write!(f, "c_ulonglong"), + Self::Float => write!(f, "c_float"), + Self::Double => write!(f, "c_double"), + Self::F32 => write!(f, "f32"), + Self::F64 => write!(f, "f64"), + Self::I8 => write!(f, "i8"), + Self::U8 => write!(f, "u8"), + Self::I16 => write!(f, "i16"), + Self::U16 => write!(f, "u16"), + Self::I32 => write!(f, "i32"), + Self::U32 => write!(f, "u32"), + Self::I64 => write!(f, "i64"), + Self::U64 => write!(f, "u64"), + // TODO: Use core::ffi::c_ssize_t + Self::ISize => write!(f, "isize"), + // TODO: Use core::ffi::c_size_t + Self::USize => write!(f, "usize"), + Self::ObjcBool => write!(f, "Bool"), + Self::NSInteger => write!(f, "NSInteger"), + Self::NSUInteger => write!(f, "NSUInteger"), + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +enum Inner { + Primitive(Primitive), // Objective-C Id { @@ -492,7 +540,6 @@ enum Inner { Sel { nullability: Nullability, }, - ObjcBool, // Others Pointer { @@ -611,24 +658,23 @@ impl Inner { } }; - use TypeKind::*; match ty.get_kind() { - Void => Self::Void, - Bool => Self::C99Bool, - CharS | CharU => Self::Char, - SChar => Self::SChar, - UChar => Self::UChar, - Short => Self::Short, - UShort => Self::UShort, - Int => Self::Int, - UInt => Self::UInt, - Long => Self::Long, - ULong => Self::ULong, - LongLong => Self::LongLong, - ULongLong => Self::ULongLong, - Float => Self::Float, - Double => Self::Double, - Record => { + TypeKind::Void => Self::Primitive(Primitive::Void), + TypeKind::Bool => Self::Primitive(Primitive::C99Bool), + TypeKind::CharS | TypeKind::CharU => Self::Primitive(Primitive::Char), + TypeKind::SChar => Self::Primitive(Primitive::SChar), + TypeKind::UChar => Self::Primitive(Primitive::UChar), + TypeKind::Short => Self::Primitive(Primitive::Short), + TypeKind::UShort => Self::Primitive(Primitive::UShort), + TypeKind::Int => Self::Primitive(Primitive::Int), + TypeKind::UInt => Self::Primitive(Primitive::UInt), + TypeKind::Long => Self::Primitive(Primitive::Long), + TypeKind::ULong => Self::Primitive(Primitive::ULong), + TypeKind::LongLong => Self::Primitive(Primitive::LongLong), + TypeKind::ULongLong => Self::Primitive(Primitive::ULongLong), + TypeKind::Float => Self::Primitive(Primitive::Float), + TypeKind::Double => Self::Primitive(Primitive::Double), + TypeKind::Record => { let declaration = ty.get_declaration().expect("record declaration"); let name = ty .get_display_name() @@ -638,7 +684,7 @@ impl Inner { id: ItemIdentifier::with_name(name, &declaration, context), } } - Enum => { + TypeKind::Enum => { let declaration = ty.get_declaration().expect("enum declaration"); let name = ty .get_display_name() @@ -648,7 +694,7 @@ impl Inner { id: ItemIdentifier::with_name(name, &declaration, context), } } - ObjCId => { + TypeKind::ObjCId => { let mut parser = AttributeParser::new(&attributed_name, "id"); lifetime.update(parser.lifetime(ParsePosition::Prefix)); @@ -672,7 +718,7 @@ impl Inner { nullability, } } - ObjCClass => { + TypeKind::ObjCClass => { let mut parser = AttributeParser::new(&attributed_name, &name); let _lifetime = parser.lifetime(ParsePosition::Suffix); let nullability = if let Some(nullability) = unexposed_nullability { @@ -682,7 +728,7 @@ impl Inner { }; Self::Class { nullability } } - ObjCSel => { + TypeKind::ObjCSel => { let mut parser = AttributeParser::new(&attributed_name, &name); let nullability = if let Some(nullability) = unexposed_nullability { nullability @@ -693,13 +739,13 @@ impl Inner { } // These can appear without being wrapped in a pointer by being in typedefs // TODO: Emit this properly. - ObjCObject => { + TypeKind::ObjCObject => { let decl = ty.get_declaration().expect("ObjCObject declaration"); Self::TypeDef { id: ItemIdentifier::with_name("AnyObject".to_string(), &decl, context), } } - Pointer => { + TypeKind::Pointer => { let mut parser = AttributeParser::new(&attributed_name, &name); let pointee = ty.get_pointee_type().expect("pointer to have pointee"); if let TypeKind::FunctionPrototype = pointee.get_kind() { @@ -720,7 +766,7 @@ impl Inner { pointee: Box::new(pointee), } } - BlockPointer => { + TypeKind::BlockPointer => { let mut parser = AttributeParser::new(&attributed_name, &name); parser.set_fn_ptr(); @@ -752,7 +798,7 @@ impl Inner { pointee => panic!("unexpected pointee in block: {pointee:?}"), } } - ObjCObjectPointer => { + TypeKind::ObjCObjectPointer => { let mut parser = AttributeParser::new(&attributed_name, &name); let is_kindof = parser.is_kindof(ParsePosition::Prefix); @@ -814,7 +860,7 @@ impl Inner { nullability, } } - Typedef => { + TypeKind::Typedef => { let typedef_name = ty.get_typedef_name().expect("typedef has name"); let mut parser = AttributeParser::new(&attributed_name, &typedef_name); @@ -858,33 +904,36 @@ impl Inner { }; match &*typedef_name { - "BOOL" => Self::ObjcBool, - - "int8_t" => Self::I8, - "uint8_t" => Self::U8, - "int16_t" => Self::I16, - "uint16_t" => Self::U16, - "int32_t" => Self::I32, - "uint32_t" => Self::U32, - "int64_t" => Self::I64, - "uint64_t" => Self::U64, - "ssize_t" => Self::ISize, - "size_t" => Self::USize, + "BOOL" => Self::Primitive(Primitive::ObjcBool), + + "int8_t" => Self::Primitive(Primitive::I8), + "uint8_t" => Self::Primitive(Primitive::U8), + "int16_t" => Self::Primitive(Primitive::I16), + "uint16_t" => Self::Primitive(Primitive::U16), + "int32_t" => Self::Primitive(Primitive::I32), + "uint32_t" => Self::Primitive(Primitive::U32), + "int64_t" => Self::Primitive(Primitive::I64), + "uint64_t" => Self::Primitive(Primitive::U64), + "ssize_t" => Self::Primitive(Primitive::ISize), + "size_t" => Self::Primitive(Primitive::USize), // MacTypes.h - "UInt8" => Self::U8, - "UInt16" => Self::U16, - "UInt32" => Self::U32, - "UInt64" => Self::U64, - "SInt8" => Self::I8, - "SInt16" => Self::I16, - "SInt32" => Self::I32, - "SInt64" => Self::I64, - "Float32" => Self::F32, - "Float64" => Self::F64, + "UInt8" => Self::Primitive(Primitive::U8), + "UInt16" => Self::Primitive(Primitive::U16), + "UInt32" => Self::Primitive(Primitive::U32), + "UInt64" => Self::Primitive(Primitive::U64), + "SInt8" => Self::Primitive(Primitive::I8), + "SInt16" => Self::Primitive(Primitive::I16), + "SInt32" => Self::Primitive(Primitive::I32), + "SInt64" => Self::Primitive(Primitive::I64), + "Float32" => Self::Primitive(Primitive::F32), + "Float64" => Self::Primitive(Primitive::F64), "Float80" => panic!("can't handle 80 bit MacOS float"), "Float96" => panic!("can't handle 96 bit 68881 float"), + "NSInteger" => Self::Primitive(Primitive::NSInteger), + "NSUInteger" => Self::Primitive(Primitive::NSUInteger), + "instancetype" => Self::Id { ty: IdType::Self_, is_const, @@ -897,7 +946,7 @@ impl Inner { let _span = debug_span!("typedef", ?typedef_name, ?canonical, ?declaration) .entered(); match canonical.get_kind() { - ObjCObjectPointer => { + TypeKind::ObjCObjectPointer => { let pointee = canonical .get_pointee_type() .expect("pointer type to have pointee"); @@ -950,7 +999,7 @@ impl Inner { } } } - FunctionPrototype => { + TypeKind::FunctionPrototype => { let call_conv = ty.get_calling_convention().expect("fn calling convention"); assert_eq!( call_conv, @@ -975,7 +1024,7 @@ impl Inner { result_type: Box::new(result_type), } } - IncompleteArray => { + TypeKind::IncompleteArray => { let mut parser = AttributeParser::new(&attributed_name, &name); parser.set_incomplete_array(); @@ -998,7 +1047,7 @@ impl Inner { pointee: Box::new(pointee), } } - ConstantArray => { + TypeKind::ConstantArray => { let mut parser = AttributeParser::new(&attributed_name, &name); parser.set_constant_array(); let _is_const = get_is_const(parser.is_const(ParsePosition::Suffix)); @@ -1034,12 +1083,13 @@ impl Inner { fn visit_required_types(&self, f: &mut impl FnMut(&ItemIdentifier)) { match self { + Self::Primitive(_) => {} // Objective-C Self::Id { ty, .. } => { // f("objc2"); ty.visit_required_types(f); } - Self::Class { .. } | Self::Sel { .. } | Self::ObjcBool => { + Self::Class { .. } | Self::Sel { .. } => { // f("objc2"); } @@ -1102,41 +1152,11 @@ impl Inner { /// required. impl fmt::Display for Inner { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - use Inner::*; match self { - // Primitives - Void => write!(f, "c_void"), - C99Bool => write!(f, "bool"), - Char => write!(f, "c_char"), - SChar => write!(f, "c_schar"), - UChar => write!(f, "c_uchar"), - Short => write!(f, "c_short"), - UShort => write!(f, "c_ushort"), - Int => write!(f, "c_int"), - UInt => write!(f, "c_uint"), - Long => write!(f, "c_long"), - ULong => write!(f, "c_ulong"), - LongLong => write!(f, "c_longlong"), - ULongLong => write!(f, "c_ulonglong"), - Float => write!(f, "c_float"), - Double => write!(f, "c_double"), - F32 => write!(f, "f32"), - F64 => write!(f, "f64"), - I8 => write!(f, "i8"), - U8 => write!(f, "u8"), - I16 => write!(f, "i16"), - U16 => write!(f, "u16"), - I32 => write!(f, "i32"), - U32 => write!(f, "u32"), - I64 => write!(f, "i64"), - U64 => write!(f, "u64"), - // TODO: Use core::ffi::c_ssize_t - ISize => write!(f, "isize"), - // TODO: Use core::ffi::c_size_t - USize => write!(f, "usize"), + Self::Primitive(prim) => write!(f, "{prim}"), // Objective-C - Id { + Self::Id { ty, is_const, // Ignore @@ -1151,24 +1171,23 @@ impl fmt::Display for Inner { write!(f, "*mut {ty}") } } - Class { nullability } => { + Self::Class { nullability } => { if *nullability == Nullability::NonNull { write!(f, "NonNull") } else { write!(f, "*const AnyClass") } } - Sel { nullability } => { + Self::Sel { nullability } => { if *nullability == Nullability::NonNull { write!(f, "Sel") } else { write!(f, "Option") } } - ObjcBool => write!(f, "Bool"), // Others - Pointer { + Self::Pointer { nullability, is_const, pointee, @@ -1191,7 +1210,7 @@ impl fmt::Display for Inner { } write!(f, ")")?; match &**result_type { - Self::Void => { + Self::Primitive(Primitive::Void) => { // Don't output anything } ty => write!(f, " -> {ty}")?, @@ -1211,7 +1230,7 @@ impl fmt::Display for Inner { } } }, - IncompleteArray { + Self::IncompleteArray { nullability, is_const, pointee, @@ -1224,11 +1243,13 @@ impl fmt::Display for Inner { write!(f, "*mut {pointee}") } } - Array { + Self::Array { element_type, num_elements, } => write!(f, "ArrayUnknownABI<[{element_type}; {num_elements}]>"), - Enum { id } | Struct { id } | TypeDef { id } => write!(f, "{}", id.path()), + Self::Enum { id } | Self::Struct { id } | Self::TypeDef { id } => { + write!(f, "{}", id.path()) + } Self::Fn { .. } => write!(f, "TodoFunction"), Self::Block { sendable: _, @@ -1242,7 +1263,7 @@ impl fmt::Display for Inner { } write!(f, ")")?; match &**result_type { - Self::Void => {} + Self::Primitive(Primitive::Void) => {} ty => write!(f, " -> {ty}")?, } if *no_escape { @@ -1286,7 +1307,7 @@ pub struct Ty { impl Ty { pub const VOID_RESULT: Self = Self { - ty: Inner::Void, + ty: Inner::Primitive(Primitive::Void), kind: TyKind::MethodReturn { with_error: false }, }; @@ -1417,7 +1438,7 @@ impl Ty { Inner::Struct { id } if id.name == typedef_name => None, // Opaque structs Inner::Pointer { pointee, .. } if matches!(&**pointee, Inner::Struct { .. }) => { - **pointee = Inner::Void; + **pointee = Inner::Primitive(Primitive::Void); Some(Self { ty, kind: TyKind::Typedef, @@ -1529,6 +1550,10 @@ impl Ty { error!(?ty, "unexpected lifetime in enum"); }); + if !matches!(ty, Inner::Primitive(_)) { + warn!(?ty, "enum type not a primitive"); + } + Self { ty, kind: TyKind::Enum, @@ -1681,7 +1706,7 @@ impl fmt::Display for Ty { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match &self.kind { TyKind::MethodReturn { with_error: false } => { - if let Inner::Void = &self.ty { + if let Inner::Primitive(Primitive::Void) = &self.ty { // Don't output anything return Ok(()); } @@ -1710,11 +1735,11 @@ impl fmt::Display for Ty { write!(f, "Option<&'static AnyClass>") } } - Inner::C99Bool => { + Inner::Primitive(Primitive::C99Bool) => { warn!("C99's bool as Objective-C method return is ill supported"); write!(f, "bool") } - Inner::ObjcBool => write!(f, "bool"), + Inner::Primitive(Primitive::ObjcBool) => write!(f, "bool"), ty => write!(f, "{ty}"), } } @@ -1732,7 +1757,7 @@ impl fmt::Display for Ty { ItemIdentifier::nserror().path(), ) } - Inner::ObjcBool => { + Inner::Primitive(Primitive::ObjcBool) => { // NO -> error write!( f, @@ -1813,10 +1838,10 @@ impl fmt::Display for Ty { write!(f, "Option<&AnyClass>") } } - Inner::C99Bool if self.kind == TyKind::MethodArgument => { + Inner::Primitive(Primitive::C99Bool) if self.kind == TyKind::MethodArgument => { panic!("C99's bool as Objective-C method argument is unsupported") } - Inner::ObjcBool if self.kind == TyKind::MethodArgument => { + Inner::Primitive(Primitive::ObjcBool) if self.kind == TyKind::MethodArgument => { write!(f, "bool") } ty @ Inner::Pointer { @@ -1865,7 +1890,7 @@ impl fmt::Display for Ty { }, TyKind::Enum => write!(f, "{}", self.ty), TyKind::FnReturn => { - if let Inner::Void = &self.ty { + if let Inner::Primitive(Primitive::Void) = &self.ty { // Don't output anything return Ok(()); } From bf030c135375ac951d51254a85d8aac492e5feb4 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Wed, 7 Feb 2024 13:51:41 +0100 Subject: [PATCH 4/4] Add a few missing `MainThreadMarker`s --- crates/header-translator/src/rust_type.rs | 6 +++++- crates/icrate/CHANGELOG.md | 2 ++ crates/icrate/src/generated | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/crates/header-translator/src/rust_type.rs b/crates/header-translator/src/rust_type.rs index 6b4133888..862fd7d00 100644 --- a/crates/header-translator/src/rust_type.rs +++ b/crates/header-translator/src/rust_type.rs @@ -1129,7 +1129,11 @@ impl Inner { pub fn visit_toplevel_types(&self, f: &mut impl FnMut(&ItemIdentifier)) { match self { - Self::Id { ty, .. } => { + Self::Id { + ty, + nullability: Nullability::NonNull, + .. + } => { ty.visit_toplevel_types(f); } Self::Pointer { diff --git a/crates/icrate/CHANGELOG.md b/crates/icrate/CHANGELOG.md index 0004f47c9..677b13409 100644 --- a/crates/icrate/CHANGELOG.md +++ b/crates/icrate/CHANGELOG.md @@ -47,6 +47,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ### Fixed * Fixed the `QuartzCore` and `Photos` frameworks not being loaded correctly. * Fixed a few feature gates on methods showing up unnecessarily. +* **BREAKING**: Added `MainThreadMarker` parameter to a few methods where it + was erroneously missing. ## icrate 0.1.0 - 2023-12-23 diff --git a/crates/icrate/src/generated b/crates/icrate/src/generated index 9074b1549..00d60f202 160000 --- a/crates/icrate/src/generated +++ b/crates/icrate/src/generated @@ -1 +1 @@ -Subproject commit 9074b15495d2ef82db10346161ebcfeec990b380 +Subproject commit 00d60f2025281cdbc238667e5ecbad7a500f0465