Skip to content

Commit 6061ff8

Browse files
authored
Merge pull request #756 from carlossless
Add config option to override method argument and return type null-ability.
2 parents 2c37327 + 1023d50 commit 6061ff8

File tree

4 files changed

+109
-9
lines changed

4 files changed

+109
-9
lines changed

crates/header-translator/src/config.rs

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -572,6 +572,22 @@ pub struct CategoryData {
572572
pub renamed: Option<String>,
573573
}
574574

575+
#[derive(Deserialize, Debug, Default, Clone, PartialEq, Eq)]
576+
#[serde(deny_unknown_fields)]
577+
#[serde(rename_all = "lowercase")]
578+
pub enum Nullability {
579+
#[default]
580+
Nullable,
581+
NonNull,
582+
}
583+
584+
#[derive(Deserialize, Debug, Default, Clone, PartialEq, Eq)]
585+
#[serde(deny_unknown_fields)]
586+
pub struct TypeOverride {
587+
#[serde(default)]
588+
pub nullability: Option<Nullability>,
589+
}
590+
575591
#[derive(Deserialize, Debug, Default, Clone, PartialEq, Eq)]
576592
#[serde(deny_unknown_fields)]
577593
pub struct MethodData {
@@ -582,6 +598,12 @@ pub struct MethodData {
582598
#[serde(rename = "unsafe")]
583599
#[serde(default)]
584600
pub unsafe_: Unsafe,
601+
#[serde(default)]
602+
#[serde(deserialize_with = "deserialize_argument_overrides")]
603+
pub arguments: HashMap<usize, TypeOverride>,
604+
#[serde(rename = "return")]
605+
#[serde(default)]
606+
pub return_: TypeOverride,
585607
}
586608

587609
impl MethodData {
@@ -591,6 +613,8 @@ impl MethodData {
591613
unsafe_: self.unsafe_,
592614
renamed: self.renamed.or(superclass.renamed).clone(),
593615
skipped: self.skipped | superclass.skipped,
616+
arguments: self.arguments,
617+
return_: self.return_,
594618
}
595619
}
596620
}
@@ -689,3 +713,32 @@ impl<'de> de::Deserialize<'de> for Counterpart {
689713
deserializer.deserialize_str(CounterpartVisitor)
690714
}
691715
}
716+
717+
fn deserialize_argument_overrides<'de, D>(
718+
deserializer: D,
719+
) -> Result<HashMap<usize, TypeOverride>, D::Error>
720+
where
721+
D: de::Deserializer<'de>,
722+
{
723+
let str_map = HashMap::<&str, TypeOverride>::deserialize(deserializer)?;
724+
let original_len = str_map.len();
725+
let data = {
726+
str_map
727+
.into_iter()
728+
.map(|(str_key, value)| match str_key.parse() {
729+
Ok(int_key) => Ok((int_key, value)),
730+
Err(_) => Err({
731+
de::Error::invalid_value(
732+
de::Unexpected::Str(str_key),
733+
&"a non-negative integer",
734+
)
735+
}),
736+
})
737+
.collect::<Result<HashMap<_, _>, _>>()?
738+
};
739+
// multiple strings could parse to the same int, e.g "0" and "00"
740+
if data.len() < original_len {
741+
return Err(de::Error::custom("duplicate integer key"));
742+
}
743+
Ok(data)
744+
}

crates/header-translator/src/method.rs

Lines changed: 52 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1+
use core::panic;
12
use std::fmt;
23

34
use clang::{Entity, EntityKind, ObjCAttributes, ObjCQualifiers};
45

56
use crate::availability::Availability;
6-
use crate::config::MethodData;
7+
use crate::config::{self, MethodData, TypeOverride};
78
use crate::context::Context;
89
use crate::display_helper::FormatterFn;
910
use crate::documentation::Documentation;
@@ -437,7 +438,8 @@ impl Method {
437438
.get_arguments()
438439
.expect("method arguments")
439440
.into_iter()
440-
.map(|entity| {
441+
.enumerate()
442+
.map(|(index, entity)| {
441443
let name = entity.get_name().expect("arg display name");
442444
let _span = debug_span!("method argument", name).entered();
443445
let qualifier = entity
@@ -473,12 +475,24 @@ impl Method {
473475
});
474476

475477
let ty = entity.get_type().expect("argument type");
476-
let ty = Ty::parse_method_argument(ty, qualifier, sendable, no_escape, context);
478+
let mut ty = Ty::parse_method_argument(ty, qualifier, sendable, no_escape, context);
479+
480+
if let Some(ty_or) = data.arguments.get(&index) {
481+
apply_type_override(&mut ty, ty_or);
482+
}
477483

478484
(name, ty)
479485
})
480486
.collect();
481487

488+
let last_arg_override = *data.arguments.keys().max().unwrap_or(&0);
489+
if arguments.len() < last_arg_override {
490+
panic!(
491+
"argument override index out of bounds {}",
492+
last_arg_override
493+
);
494+
}
495+
482496
let is_error = if let Some((_, ty)) = arguments.last() {
483497
ty.argument_is_error_out()
484498
} else {
@@ -531,6 +545,8 @@ impl Method {
531545
modifiers.mainthreadonly,
532546
);
533547

548+
apply_type_override(&mut result_type, &data.return_);
549+
534550
let encoding = entity
535551
.get_objc_type_encoding()
536552
.expect("method to have encoding");
@@ -605,7 +621,7 @@ impl Method {
605621
.expect("method to have encoding");
606622

607623
let getter = if !getter_data.skipped {
608-
let ty = Ty::parse_property_return(
624+
let mut ty = Ty::parse_property_return(
609625
entity.get_type().expect("property type"),
610626
is_copy,
611627
modifiers.sendable,
@@ -627,6 +643,8 @@ impl Method {
627643
modifiers.mainthreadonly,
628644
);
629645

646+
apply_type_override(&mut ty, &getter_data.return_);
647+
630648
Some(Method {
631649
selector: getter_sel.clone(),
632650
fn_name: getter_sel.clone(),
@@ -655,7 +673,7 @@ impl Method {
655673
let setter_data = setter_data.expect("setter_data must be present if setter_sel was");
656674
if !setter_data.skipped {
657675
let result_type = Ty::VOID_RESULT;
658-
let ty = Ty::parse_property(
676+
let mut ty = Ty::parse_property(
659677
entity.get_type().expect("property type"),
660678
is_copy,
661679
modifiers.sendable,
@@ -674,6 +692,10 @@ impl Method {
674692
modifiers.mainthreadonly,
675693
);
676694

695+
if let Some(ty_or) = setter_data.arguments.get(&0) {
696+
apply_type_override(&mut ty, ty_or);
697+
}
698+
677699
Some(Method {
678700
selector,
679701
fn_name,
@@ -909,3 +931,28 @@ pub(crate) fn handle_reserved(name: &str) -> String {
909931
name.into()
910932
}
911933
}
934+
935+
pub(crate) fn apply_type_override(ty: &mut Ty, or: &TypeOverride) {
936+
if let Some(nullability) = &or.nullability {
937+
let c_nullability = match nullability {
938+
config::Nullability::Nullable => clang::Nullability::Nullable,
939+
config::Nullability::NonNull => clang::Nullability::NonNull,
940+
};
941+
942+
let check_and_set_nullability = |current_nullability: &mut clang::Nullability| {
943+
if *current_nullability == c_nullability {
944+
warn!("nullability already set to {:?}", current_nullability);
945+
}
946+
*current_nullability = c_nullability;
947+
};
948+
949+
match ty {
950+
Ty::Pointer { nullability, .. }
951+
| Ty::Sel { nullability, .. }
952+
| Ty::IncompleteArray { nullability, .. } => {
953+
check_and_set_nullability(nullability);
954+
}
955+
_ => panic!("unexpected type: {:?}", ty),
956+
};
957+
}
958+
}

framework-crates/objc2-io-usb-host/translation-config.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ custom-lib-rs = true
55
macos = "10.15"
66
maccatalyst = "14.0"
77

8-
# Return type marked as non-null, raises `unknown error result type`.
9-
class.IOUSBHostControllerInterface.methods."getPortStateMachineForCommand:error:".skipped = true
10-
class.IOUSBHostControllerInterface.methods."getPortStateMachineForPort:error:".skipped = true
8+
# Return type nullability is incorrectly declared in the header. It should be `nullable` instead of `nonnull`.
9+
class.IOUSBHostControllerInterface.methods."getPortStateMachineForCommand:error:".return.nullability = "nullable"
10+
class.IOUSBHostControllerInterface.methods."getPortStateMachineForPort:error:".return.nullability = "nullable"
1111

1212
# Returns non-object pointer in error method, raises `unknown error result type`.
1313
class.IOUSBHostObject.methods."descriptorWithType:length:index:languageID:requestType:requestRecipient:error:".skipped = true

generated

0 commit comments

Comments
 (0)