From 084bf72e620623d3464752cd82ce02665f29a977 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Mon, 12 Oct 2026 09:32:29 -0700 Subject: [PATCH 01/12] Stub parser: add test case for verification with only functions --- .../verification-cases/functions.expected | 215 ++++++++++++++++++ .../verification-cases/functions.stub.php | 7 + 2 files changed, 222 insertions(+) create mode 100644 crates/stub-parser/tests/verification-cases/functions.expected create mode 100644 crates/stub-parser/tests/verification-cases/functions.stub.php diff --git a/crates/stub-parser/tests/verification-cases/functions.expected b/crates/stub-parser/tests/verification-cases/functions.expected new file mode 100644 index 0000000..fbd8fd9 --- /dev/null +++ b/crates/stub-parser/tests/verification-cases/functions.expected @@ -0,0 +1,215 @@ +use rsphp_macros_c::*; +use rsphp_macros_rs::*; +use rsphp_sys::*; +#[cfg(debug_assertions)] +use std::ffi::{c_char, CStr}; + +/// Check if two type lists match. +#[cfg(debug_assertions)] +fn type_lists_match(expected: &zend_type_list, actual: &zend_type_list) -> bool { + if expected.num_types != actual.num_types { + return false; + } + let expected_types: &[zend_type] = unsafe { + std::slice::from_raw_parts( + expected.types.as_ptr(), + expected.num_types.try_into().expect("Valid as a usize"), + ) + }; + let actual_types: &[zend_type] = unsafe { + std::slice::from_raw_parts(actual.types.as_ptr(), actual.num_types.try_into().expect("Valid as a usize")) + }; + expected_types.iter().zip(actual_types).all(|(e, a)| types_match(e, a).is_none()) +} + +/// Check if two types match, returning an error message with some details if +/// they do not. +#[expect(clippy::unwrap_in_result, reason = "Failures are Some(s) rather than None, and startup code should panic")] +#[cfg(debug_assertions)] +fn types_match(expected: &zend_type, actual: &zend_type) -> Option { + if expected.type_mask != actual.type_mask { + return Some(format!("Expected type mask 0x{:x}, found 0x{:x}", expected.type_mask, actual.type_mask)); + } + if expected.ptr.is_null() { + if actual.ptr.is_null() { + return None; + } + return Some("Expected no pointer to be set in actual type".to_owned()); + } + if actual.ptr.is_null() { + return Some("Expected a pointer to be set in the actual type".to_owned()); + } + // Compare the values depending on the flags + if expected.type_mask & _ZEND_TYPE_LITERAL_NAME_BIT != 0 { + // CString pointers + let expected_name = unsafe { CStr::from_ptr(expected.ptr.cast::()) }; + let actual_name = unsafe { CStr::from_ptr(actual.ptr.cast::()) }; + if expected_name == actual_name { + return None; + } + return Some(format!("Expected literal name {expected_name:?}, found {actual_name:?}")); + } + if expected.type_mask & _ZEND_TYPE_NAME_BIT != 0 { + // zend_string pointers, compare them by comparing the lengths and values + let expected_name: &zend_string = unsafe { expected.ptr.cast::().as_ref().expect("Valid pointer") }; + let actual_name: &zend_string = unsafe { actual.ptr.cast::().as_ref().expect("Valid pointer") }; + if expected_name.len != actual_name.len { + return Some(format!("Expected name of length {}, found name of length {}", expected_name.len, actual_name.len)); + } + let expected_slice: &[c_char] = unsafe { std::slice::from_raw_parts(expected_name.val.as_ptr(), expected_name.len) }; + let actual_slice: &[c_char] = unsafe { std::slice::from_raw_parts(actual_name.val.as_ptr(), actual_name.len) }; + if expected_slice == actual_slice { + return None; + } + return Some(format!("Expected name {expected_slice:?}, found {actual_slice:?}")); + } + if expected.type_mask & _ZEND_TYPE_LIST_BIT != 0 { + // zend_type_list pointers + let expected_list: &zend_type_list = unsafe { expected.ptr.cast::().as_ref().expect("Valid pointer") }; + let acutal_list: &zend_type_list = unsafe { actual.ptr.cast::().as_ref().expect("Valid pointer") }; + if type_lists_match(expected_list, acutal_list) { + return None; + } + return Some("Type lists differ".to_owned()); + } + panic!("Unhandled zend types, unknown mask type: {}", expected.type_mask); +} + +/// Verify a function argument information matches the expected information, +/// returning an error of errors. +#[cfg(debug_assertions)] +fn verify_arg( + idx: usize, + method: &str, + expected: &zend_internal_arg_info, + actual: &zend_internal_arg_info, +) -> Vec { + let mut errors: Vec = vec![]; + if idx == 0 { + // Dummy entry at the start with information for the overall function + assert!(expected.default_value.is_null(), "{method}: expected arg info for function registration cannot have a default value"); + if !actual.default_value.is_null() { + errors.push(format!("{method}: Arg info for function registration has an unexpected default value")); + } + // `name` is abused as the number of required arguments + let expected_req_args = expected.name.addr(); + let actual_req_args = actual.name.addr(); + if expected_req_args != actual_req_args { + errors.push(format!("{method}: expected {expected_req_args} required arguments, found {actual_req_args}")); + } + if let Some(error) = types_match(&expected.type_, &actual.type_) { + errors.push(format!("{method}: return types differ: {error}")); + } + return errors; + } + let expected_name = unsafe { CStr::from_ptr(expected.name) }; + if actual.name.is_null() { + errors.push(format!("{method}: argument {} is missing a name", idx - 1)); + } else { + let actual_name = unsafe { CStr::from_ptr(actual.name) }; + if expected_name != actual_name { + errors.push(format!("{method}: argument {} expected to be named {expected_name:?}, actually named {actual_name:?}", idx - 1)); + } + } + if let Some(error) = types_match(&expected.type_, &actual.type_) { + errors.push(format!("{method}: argument {} type does not match expected: {error}", idx - 1)); + } + match (expected.default_value.is_null(), actual.default_value.is_null()) { + (true, true) => (), + (true, false) => { + let actual_default = unsafe { CStr::from_ptr(actual.default_value) }; + errors.push(format!("{method}: argument {} has unexpected default: {actual_default:?}", idx - 1)); + } + (false, true) => { + let expected_default = unsafe { CStr::from_ptr(expected.default_value) }; + errors.push(format!("{method}: argument {} missing expected default: {expected_default:?}", idx - 1)); + } + (false, false) => { + let actual_default = unsafe { CStr::from_ptr(actual.default_value) }; + let expected_default = unsafe { CStr::from_ptr(expected.default_value) }; + if actual_default != expected_default { + errors.push(format!( + "{method}: argument {} expected to have default {expected_default:?}, found {actual_default:?}", + idx - 1 + )); + } + } + } + errors +} + +/// Verify the registration of a function or method. +#[cfg(debug_assertions)] +fn verify_registration( + debug_name: &str, + expected_methods: &[(&str, &[zend_internal_arg_info], u32)], + actual_methods: &[zend_function_entry], +) { + let mut remaining_methods = actual_methods.to_vec(); + // Must end with the results of ZEND_FE_END + // So that we can use unwrapping below, do these assertions immediately + let last = remaining_methods.pop().unwrap_or_else(|| panic!("{debug_name} registration should end with ZEND_FE_END")); + assert!(last.fname.is_null(), "{debug_name} registration should end with ZEND_FE_END"); + assert!(last.handler.is_none(), "{debug_name} registration should end with ZEND_FE_END"); + assert!(last.arg_info.is_null(), "{debug_name} registration should end with ZEND_FE_END"); + assert_eq!(0, last.num_args, "{debug_name} registration should end with ZEND_FE_END"); + assert_eq!(0, last.flags, "{debug_name} registration should end with ZEND_FE_END"); + assert!(last.frameless_function_infos.is_null(), "{debug_name} registration should end with ZEND_FE_END"); + assert!(last.doc_comment.is_null(), "{debug_name} registration should end with ZEND_FE_END"); + + // So that we don't need to keep converting from char pointers + let mut methods_and_names: Vec<_> = remaining_methods.into_iter().map( + |fn_entry| ( + unsafe { CStr::from_ptr(fn_entry.fname) }.to_str().expect("Should be a valid method name"), + fn_entry + ) + ).collect(); + let mut errors: Vec = vec![]; + for &(name, expect_args, expect_flags) in expected_methods { + let index_opt = methods_and_names + .iter() + .position(|&(fn_name, _)| fn_name == name); + let Some(index) = index_opt else { + errors.push(format!("Missing registration of method {name}")); + continue; + }; + let (_, fn_entry) = methods_and_names.remove(index); + if fn_entry.flags != expect_flags { + errors.push(format!( + "Registration of method {name} has wrong flags, expected {expect_flags}, found {}", + fn_entry.flags + )); + } + if fn_entry.arg_info == expect_args.as_ptr() { + continue; + } + // Rust global `const` values are *not* guaranteed to refer to the same + // location in memory because they might be inlined; compare the actual values + if fn_entry.num_args + 1 != expect_args.len().try_into().expect("Valid as a usize") { + errors.push( + format!("Registration of method {name} has wrong arg info, expected {} args, found {}", + expect_args.len() - 1, fn_entry.num_args) + ); + continue; + } + let fn_entry_arginfo = unsafe { + std::slice::from_raw_parts( + fn_entry.arg_info, + (fn_entry.num_args + 1).try_into().expect("Valid as a usize"), + ) + }; + for (idx, (expected, found)) in expect_args.iter().zip(fn_entry_arginfo).enumerate() { + errors.extend(verify_arg(idx, name, expected, found)); + } + } + for (name, _) in methods_and_names { + errors.push(format!("Unexpected registration of method {name}")); + } + let expected: &[String] = &[]; + assert_eq!(expected, errors, "Registration of {debug_name} should be correct"); +} +/// Argument information for `test1()`. +pub(crate) const ARGINFO_TEST1: [zend_internal_arg_info; 1] = [ + ZEND_ARG_INFO_FOR_FUNCTION_WITH_RETURN_TYPE_MASK!(0, 0, MAY_BE_VOID, 0), +]; + diff --git a/crates/stub-parser/tests/verification-cases/functions.stub.php b/crates/stub-parser/tests/verification-cases/functions.stub.php new file mode 100644 index 0000000..1b2f623 --- /dev/null +++ b/crates/stub-parser/tests/verification-cases/functions.stub.php @@ -0,0 +1,7 @@ + Date: Mon, 12 Oct 2026 09:41:51 -0700 Subject: [PATCH 02/12] Stub parser: add test case for verification with class without methods --- .../verification-cases/no_methods.expected | 321 ++++++++++++++++++ .../verification-cases/no_methods.stub.php | 23 ++ 2 files changed, 344 insertions(+) create mode 100644 crates/stub-parser/tests/verification-cases/no_methods.expected create mode 100644 crates/stub-parser/tests/verification-cases/no_methods.stub.php diff --git a/crates/stub-parser/tests/verification-cases/no_methods.expected b/crates/stub-parser/tests/verification-cases/no_methods.expected new file mode 100644 index 0000000..e045237 --- /dev/null +++ b/crates/stub-parser/tests/verification-cases/no_methods.expected @@ -0,0 +1,321 @@ +use rsphp_macros_c::*; +use rsphp_macros_rs::*; +use rsphp_sys::*; +#[cfg(debug_assertions)] +use std::ffi::{c_char, CStr}; + +/// Check if two type lists match. +#[cfg(debug_assertions)] +fn type_lists_match(expected: &zend_type_list, actual: &zend_type_list) -> bool { + if expected.num_types != actual.num_types { + return false; + } + let expected_types: &[zend_type] = unsafe { + std::slice::from_raw_parts( + expected.types.as_ptr(), + expected.num_types.try_into().expect("Valid as a usize"), + ) + }; + let actual_types: &[zend_type] = unsafe { + std::slice::from_raw_parts(actual.types.as_ptr(), actual.num_types.try_into().expect("Valid as a usize")) + }; + expected_types.iter().zip(actual_types).all(|(e, a)| types_match(e, a).is_none()) +} + +/// Check if two types match, returning an error message with some details if +/// they do not. +#[expect(clippy::unwrap_in_result, reason = "Failures are Some(s) rather than None, and startup code should panic")] +#[cfg(debug_assertions)] +fn types_match(expected: &zend_type, actual: &zend_type) -> Option { + if expected.type_mask != actual.type_mask { + return Some(format!("Expected type mask 0x{:x}, found 0x{:x}", expected.type_mask, actual.type_mask)); + } + if expected.ptr.is_null() { + if actual.ptr.is_null() { + return None; + } + return Some("Expected no pointer to be set in actual type".to_owned()); + } + if actual.ptr.is_null() { + return Some("Expected a pointer to be set in the actual type".to_owned()); + } + // Compare the values depending on the flags + if expected.type_mask & _ZEND_TYPE_LITERAL_NAME_BIT != 0 { + // CString pointers + let expected_name = unsafe { CStr::from_ptr(expected.ptr.cast::()) }; + let actual_name = unsafe { CStr::from_ptr(actual.ptr.cast::()) }; + if expected_name == actual_name { + return None; + } + return Some(format!("Expected literal name {expected_name:?}, found {actual_name:?}")); + } + if expected.type_mask & _ZEND_TYPE_NAME_BIT != 0 { + // zend_string pointers, compare them by comparing the lengths and values + let expected_name: &zend_string = unsafe { expected.ptr.cast::().as_ref().expect("Valid pointer") }; + let actual_name: &zend_string = unsafe { actual.ptr.cast::().as_ref().expect("Valid pointer") }; + if expected_name.len != actual_name.len { + return Some(format!("Expected name of length {}, found name of length {}", expected_name.len, actual_name.len)); + } + let expected_slice: &[c_char] = unsafe { std::slice::from_raw_parts(expected_name.val.as_ptr(), expected_name.len) }; + let actual_slice: &[c_char] = unsafe { std::slice::from_raw_parts(actual_name.val.as_ptr(), actual_name.len) }; + if expected_slice == actual_slice { + return None; + } + return Some(format!("Expected name {expected_slice:?}, found {actual_slice:?}")); + } + if expected.type_mask & _ZEND_TYPE_LIST_BIT != 0 { + // zend_type_list pointers + let expected_list: &zend_type_list = unsafe { expected.ptr.cast::().as_ref().expect("Valid pointer") }; + let acutal_list: &zend_type_list = unsafe { actual.ptr.cast::().as_ref().expect("Valid pointer") }; + if type_lists_match(expected_list, acutal_list) { + return None; + } + return Some("Type lists differ".to_owned()); + } + panic!("Unhandled zend types, unknown mask type: {}", expected.type_mask); +} + +/// Verify a function argument information matches the expected information, +/// returning an error of errors. +#[cfg(debug_assertions)] +fn verify_arg( + idx: usize, + method: &str, + expected: &zend_internal_arg_info, + actual: &zend_internal_arg_info, +) -> Vec { + let mut errors: Vec = vec![]; + if idx == 0 { + // Dummy entry at the start with information for the overall function + assert!(expected.default_value.is_null(), "{method}: expected arg info for function registration cannot have a default value"); + if !actual.default_value.is_null() { + errors.push(format!("{method}: Arg info for function registration has an unexpected default value")); + } + // `name` is abused as the number of required arguments + let expected_req_args = expected.name.addr(); + let actual_req_args = actual.name.addr(); + if expected_req_args != actual_req_args { + errors.push(format!("{method}: expected {expected_req_args} required arguments, found {actual_req_args}")); + } + if let Some(error) = types_match(&expected.type_, &actual.type_) { + errors.push(format!("{method}: return types differ: {error}")); + } + return errors; + } + let expected_name = unsafe { CStr::from_ptr(expected.name) }; + if actual.name.is_null() { + errors.push(format!("{method}: argument {} is missing a name", idx - 1)); + } else { + let actual_name = unsafe { CStr::from_ptr(actual.name) }; + if expected_name != actual_name { + errors.push(format!("{method}: argument {} expected to be named {expected_name:?}, actually named {actual_name:?}", idx - 1)); + } + } + if let Some(error) = types_match(&expected.type_, &actual.type_) { + errors.push(format!("{method}: argument {} type does not match expected: {error}", idx - 1)); + } + match (expected.default_value.is_null(), actual.default_value.is_null()) { + (true, true) => (), + (true, false) => { + let actual_default = unsafe { CStr::from_ptr(actual.default_value) }; + errors.push(format!("{method}: argument {} has unexpected default: {actual_default:?}", idx - 1)); + } + (false, true) => { + let expected_default = unsafe { CStr::from_ptr(expected.default_value) }; + errors.push(format!("{method}: argument {} missing expected default: {expected_default:?}", idx - 1)); + } + (false, false) => { + let actual_default = unsafe { CStr::from_ptr(actual.default_value) }; + let expected_default = unsafe { CStr::from_ptr(expected.default_value) }; + if actual_default != expected_default { + errors.push(format!( + "{method}: argument {} expected to have default {expected_default:?}, found {actual_default:?}", + idx - 1 + )); + } + } + } + errors +} + +/// Verify the registration of a function or method. +#[cfg(debug_assertions)] +fn verify_registration( + debug_name: &str, + expected_methods: &[(&str, &[zend_internal_arg_info], u32)], + actual_methods: &[zend_function_entry], +) { + let mut remaining_methods = actual_methods.to_vec(); + // Must end with the results of ZEND_FE_END + // So that we can use unwrapping below, do these assertions immediately + let last = remaining_methods.pop().unwrap_or_else(|| panic!("{debug_name} registration should end with ZEND_FE_END")); + assert!(last.fname.is_null(), "{debug_name} registration should end with ZEND_FE_END"); + assert!(last.handler.is_none(), "{debug_name} registration should end with ZEND_FE_END"); + assert!(last.arg_info.is_null(), "{debug_name} registration should end with ZEND_FE_END"); + assert_eq!(0, last.num_args, "{debug_name} registration should end with ZEND_FE_END"); + assert_eq!(0, last.flags, "{debug_name} registration should end with ZEND_FE_END"); + assert!(last.frameless_function_infos.is_null(), "{debug_name} registration should end with ZEND_FE_END"); + assert!(last.doc_comment.is_null(), "{debug_name} registration should end with ZEND_FE_END"); + + // So that we don't need to keep converting from char pointers + let mut methods_and_names: Vec<_> = remaining_methods.into_iter().map( + |fn_entry| ( + unsafe { CStr::from_ptr(fn_entry.fname) }.to_str().expect("Should be a valid method name"), + fn_entry + ) + ).collect(); + let mut errors: Vec = vec![]; + for &(name, expect_args, expect_flags) in expected_methods { + let index_opt = methods_and_names + .iter() + .position(|&(fn_name, _)| fn_name == name); + let Some(index) = index_opt else { + errors.push(format!("Missing registration of method {name}")); + continue; + }; + let (_, fn_entry) = methods_and_names.remove(index); + if fn_entry.flags != expect_flags { + errors.push(format!( + "Registration of method {name} has wrong flags, expected {expect_flags}, found {}", + fn_entry.flags + )); + } + if fn_entry.arg_info == expect_args.as_ptr() { + continue; + } + // Rust global `const` values are *not* guaranteed to refer to the same + // location in memory because they might be inlined; compare the actual values + if fn_entry.num_args + 1 != expect_args.len().try_into().expect("Valid as a usize") { + errors.push( + format!("Registration of method {name} has wrong arg info, expected {} args, found {}", + expect_args.len() - 1, fn_entry.num_args) + ); + continue; + } + let fn_entry_arginfo = unsafe { + std::slice::from_raw_parts( + fn_entry.arg_info, + (fn_entry.num_args + 1).try_into().expect("Valid as a usize"), + ) + }; + for (idx, (expected, found)) in expect_args.iter().zip(fn_entry_arginfo).enumerate() { + errors.extend(verify_arg(idx, name, expected, found)); + } + } + for (name, _) in methods_and_names { + errors.push(format!("Unexpected registration of method {name}")); + } + let expected: &[String] = &[]; + assert_eq!(expected, errors, "Registration of {debug_name} should be correct"); +} +/// Module for `Example` class. +pub(crate) mod Example { + use rsphp_macros_c::*; + use rsphp_sys::*; + + + /// Register the `Example` class. + pub(crate) fn register_class() -> *mut zend_class_entry { + let mut ce: zend_class_entry = unsafe { std::mem::zeroed() }; + INIT_CLASS_ENTRY!(ce, "Example", NULL); + let class_entry: *mut zend_class_entry = unsafe { + zend_register_internal_class_with_flags( + std::ptr::from_mut::(&mut ce), + std::ptr::null_mut(), + ZEND_ACC_FINAL|ZEND_ACC_NO_DYNAMIC_PROPERTIES|ZEND_ACC_NOT_SERIALIZABLE, + ) + }; + + let mut property_first_default_value: zval = unsafe { std::mem::zeroed() }; + unsafe { ZVAL_UNDEF!(std::ptr::from_mut::(&mut property_first_default_value)); } + let property_first_name: *mut zend_string = unsafe { + zend_string_init( + c"first".as_ptr(), + "first".len(), + true, + ) + }; + unsafe { + zend_declare_typed_property( + class_entry, + property_first_name, + std::ptr::from_mut::(&mut property_first_default_value), + ZEND_ACC_PUBLIC as i32, + std::ptr::null_mut(), + ZEND_TYPE_INIT_MASK!(MAY_BE_STRING), + ); + } + unsafe { + zend_string_release(property_first_name); + } + + let mut property_second_default_value: zval = unsafe { std::mem::zeroed() }; + unsafe { ZVAL_UNDEF!(std::ptr::from_mut::(&mut property_second_default_value)); } + let property_second_name: *mut zend_string = unsafe { + zend_string_init( + c"second".as_ptr(), + "second".len(), + true, + ) + }; + unsafe { + zend_declare_typed_property( + class_entry, + property_second_name, + std::ptr::from_mut::(&mut property_second_default_value), + ZEND_ACC_PUBLIC as i32, + std::ptr::null_mut(), + ZEND_TYPE_INIT_MASK!(MAY_BE_STRING), + ); + } + unsafe { + zend_string_release(property_second_name); + } + + class_entry + } +} +/// Module for `MyBoolean` enum. +pub(crate) mod MyBoolean { + use rsphp_macros_c::*; + use rsphp_sys::*; + + + /// Register the `MyBoolean` enum. + pub(crate) fn register_enum() -> *mut zend_class_entry { + let class_entry: *mut zend_class_entry = unsafe { + zend_register_internal_enum( + c"MyBoolean".as_ptr(), + IS_UNDEF, + std::ptr::null_mut(), + ) + }; + unsafe { + zend_enum_add_case_cstr(class_entry, c"True".as_ptr(), std::ptr::null_mut()); + zend_enum_add_case_cstr(class_entry, c"False".as_ptr(), std::ptr::null_mut()); + }; + + + class_entry + } +} +/// Module for `Marker` interface. +pub(crate) mod Marker { + use rsphp_macros_c::*; + use rsphp_sys::*; + + + /// Register the `Marker` interface. + pub(crate) fn register_interface() -> *mut zend_class_entry { + let mut ce: zend_class_entry = unsafe { std::mem::zeroed() }; + INIT_CLASS_ENTRY!(ce, "Marker", NULL); + let class_entry: *mut zend_class_entry = unsafe { + zend_register_internal_interface( + std::ptr::from_mut::(&mut ce), + ) + }; + + + class_entry + } +} diff --git a/crates/stub-parser/tests/verification-cases/no_methods.stub.php b/crates/stub-parser/tests/verification-cases/no_methods.stub.php new file mode 100644 index 0000000..50ba48d --- /dev/null +++ b/crates/stub-parser/tests/verification-cases/no_methods.stub.php @@ -0,0 +1,23 @@ + Date: Mon, 12 Oct 2026 09:51:09 -0700 Subject: [PATCH 03/12] Stub parser: omit verification code when unused If the stub does not have any classes with methods, the verification code is not used, omit it to avoid warnings about unused code. --- crates/stub-parser/src/builder.rs | 13 +- .../verification-cases/functions.expected | 206 ------------------ .../verification-cases/no_methods.expected | 206 ------------------ 3 files changed, 12 insertions(+), 413 deletions(-) diff --git a/crates/stub-parser/src/builder.rs b/crates/stub-parser/src/builder.rs index 75327e3..39068a0 100644 --- a/crates/stub-parser/src/builder.rs +++ b/crates/stub-parser/src/builder.rs @@ -303,6 +303,10 @@ pub(crate) struct ArgInfoBuilder<'program> { namespace: Option, /// How registration functions should verify their arguments. verification: VerificationMode, + /// Whether any class/enum/interface/trait is present in the stub; if not + /// then there is nothing to verify and the verification code doesn't need + /// to be added. + needs_verification: bool, } impl<'program> ArgInfoBuilder<'program> { @@ -314,6 +318,7 @@ impl<'program> ArgInfoBuilder<'program> { parts: vec![], namespace: None, verification, + needs_verification: false, } } } @@ -750,6 +755,7 @@ impl ArgInfoBuilder<'_> { )?); } method_verifications.push(verification); + self.needs_verification = true; } ClassLikeMember::Property(property) => { properties.push(self.build_property(&property)?); @@ -1213,9 +1219,14 @@ impl ArgInfoBuilder<'_> { /// Combine all of the code parts into the arginfo file contents. pub(crate) fn assemble(&self) -> String { + let verification_code = if self.needs_verification { + self.verification.get_infra() + } else { + String::new() + }; format!( "use rsphp_macros_c::*;\nuse rsphp_macros_rs::*;\nuse rsphp_sys::*;\n{}\n{}\n", - self.verification.get_infra(), + verification_code, self.parts.join("\n") ) } diff --git a/crates/stub-parser/tests/verification-cases/functions.expected b/crates/stub-parser/tests/verification-cases/functions.expected index fbd8fd9..58a32a8 100644 --- a/crates/stub-parser/tests/verification-cases/functions.expected +++ b/crates/stub-parser/tests/verification-cases/functions.expected @@ -1,213 +1,7 @@ use rsphp_macros_c::*; use rsphp_macros_rs::*; use rsphp_sys::*; -#[cfg(debug_assertions)] -use std::ffi::{c_char, CStr}; -/// Check if two type lists match. -#[cfg(debug_assertions)] -fn type_lists_match(expected: &zend_type_list, actual: &zend_type_list) -> bool { - if expected.num_types != actual.num_types { - return false; - } - let expected_types: &[zend_type] = unsafe { - std::slice::from_raw_parts( - expected.types.as_ptr(), - expected.num_types.try_into().expect("Valid as a usize"), - ) - }; - let actual_types: &[zend_type] = unsafe { - std::slice::from_raw_parts(actual.types.as_ptr(), actual.num_types.try_into().expect("Valid as a usize")) - }; - expected_types.iter().zip(actual_types).all(|(e, a)| types_match(e, a).is_none()) -} - -/// Check if two types match, returning an error message with some details if -/// they do not. -#[expect(clippy::unwrap_in_result, reason = "Failures are Some(s) rather than None, and startup code should panic")] -#[cfg(debug_assertions)] -fn types_match(expected: &zend_type, actual: &zend_type) -> Option { - if expected.type_mask != actual.type_mask { - return Some(format!("Expected type mask 0x{:x}, found 0x{:x}", expected.type_mask, actual.type_mask)); - } - if expected.ptr.is_null() { - if actual.ptr.is_null() { - return None; - } - return Some("Expected no pointer to be set in actual type".to_owned()); - } - if actual.ptr.is_null() { - return Some("Expected a pointer to be set in the actual type".to_owned()); - } - // Compare the values depending on the flags - if expected.type_mask & _ZEND_TYPE_LITERAL_NAME_BIT != 0 { - // CString pointers - let expected_name = unsafe { CStr::from_ptr(expected.ptr.cast::()) }; - let actual_name = unsafe { CStr::from_ptr(actual.ptr.cast::()) }; - if expected_name == actual_name { - return None; - } - return Some(format!("Expected literal name {expected_name:?}, found {actual_name:?}")); - } - if expected.type_mask & _ZEND_TYPE_NAME_BIT != 0 { - // zend_string pointers, compare them by comparing the lengths and values - let expected_name: &zend_string = unsafe { expected.ptr.cast::().as_ref().expect("Valid pointer") }; - let actual_name: &zend_string = unsafe { actual.ptr.cast::().as_ref().expect("Valid pointer") }; - if expected_name.len != actual_name.len { - return Some(format!("Expected name of length {}, found name of length {}", expected_name.len, actual_name.len)); - } - let expected_slice: &[c_char] = unsafe { std::slice::from_raw_parts(expected_name.val.as_ptr(), expected_name.len) }; - let actual_slice: &[c_char] = unsafe { std::slice::from_raw_parts(actual_name.val.as_ptr(), actual_name.len) }; - if expected_slice == actual_slice { - return None; - } - return Some(format!("Expected name {expected_slice:?}, found {actual_slice:?}")); - } - if expected.type_mask & _ZEND_TYPE_LIST_BIT != 0 { - // zend_type_list pointers - let expected_list: &zend_type_list = unsafe { expected.ptr.cast::().as_ref().expect("Valid pointer") }; - let acutal_list: &zend_type_list = unsafe { actual.ptr.cast::().as_ref().expect("Valid pointer") }; - if type_lists_match(expected_list, acutal_list) { - return None; - } - return Some("Type lists differ".to_owned()); - } - panic!("Unhandled zend types, unknown mask type: {}", expected.type_mask); -} - -/// Verify a function argument information matches the expected information, -/// returning an error of errors. -#[cfg(debug_assertions)] -fn verify_arg( - idx: usize, - method: &str, - expected: &zend_internal_arg_info, - actual: &zend_internal_arg_info, -) -> Vec { - let mut errors: Vec = vec![]; - if idx == 0 { - // Dummy entry at the start with information for the overall function - assert!(expected.default_value.is_null(), "{method}: expected arg info for function registration cannot have a default value"); - if !actual.default_value.is_null() { - errors.push(format!("{method}: Arg info for function registration has an unexpected default value")); - } - // `name` is abused as the number of required arguments - let expected_req_args = expected.name.addr(); - let actual_req_args = actual.name.addr(); - if expected_req_args != actual_req_args { - errors.push(format!("{method}: expected {expected_req_args} required arguments, found {actual_req_args}")); - } - if let Some(error) = types_match(&expected.type_, &actual.type_) { - errors.push(format!("{method}: return types differ: {error}")); - } - return errors; - } - let expected_name = unsafe { CStr::from_ptr(expected.name) }; - if actual.name.is_null() { - errors.push(format!("{method}: argument {} is missing a name", idx - 1)); - } else { - let actual_name = unsafe { CStr::from_ptr(actual.name) }; - if expected_name != actual_name { - errors.push(format!("{method}: argument {} expected to be named {expected_name:?}, actually named {actual_name:?}", idx - 1)); - } - } - if let Some(error) = types_match(&expected.type_, &actual.type_) { - errors.push(format!("{method}: argument {} type does not match expected: {error}", idx - 1)); - } - match (expected.default_value.is_null(), actual.default_value.is_null()) { - (true, true) => (), - (true, false) => { - let actual_default = unsafe { CStr::from_ptr(actual.default_value) }; - errors.push(format!("{method}: argument {} has unexpected default: {actual_default:?}", idx - 1)); - } - (false, true) => { - let expected_default = unsafe { CStr::from_ptr(expected.default_value) }; - errors.push(format!("{method}: argument {} missing expected default: {expected_default:?}", idx - 1)); - } - (false, false) => { - let actual_default = unsafe { CStr::from_ptr(actual.default_value) }; - let expected_default = unsafe { CStr::from_ptr(expected.default_value) }; - if actual_default != expected_default { - errors.push(format!( - "{method}: argument {} expected to have default {expected_default:?}, found {actual_default:?}", - idx - 1 - )); - } - } - } - errors -} - -/// Verify the registration of a function or method. -#[cfg(debug_assertions)] -fn verify_registration( - debug_name: &str, - expected_methods: &[(&str, &[zend_internal_arg_info], u32)], - actual_methods: &[zend_function_entry], -) { - let mut remaining_methods = actual_methods.to_vec(); - // Must end with the results of ZEND_FE_END - // So that we can use unwrapping below, do these assertions immediately - let last = remaining_methods.pop().unwrap_or_else(|| panic!("{debug_name} registration should end with ZEND_FE_END")); - assert!(last.fname.is_null(), "{debug_name} registration should end with ZEND_FE_END"); - assert!(last.handler.is_none(), "{debug_name} registration should end with ZEND_FE_END"); - assert!(last.arg_info.is_null(), "{debug_name} registration should end with ZEND_FE_END"); - assert_eq!(0, last.num_args, "{debug_name} registration should end with ZEND_FE_END"); - assert_eq!(0, last.flags, "{debug_name} registration should end with ZEND_FE_END"); - assert!(last.frameless_function_infos.is_null(), "{debug_name} registration should end with ZEND_FE_END"); - assert!(last.doc_comment.is_null(), "{debug_name} registration should end with ZEND_FE_END"); - - // So that we don't need to keep converting from char pointers - let mut methods_and_names: Vec<_> = remaining_methods.into_iter().map( - |fn_entry| ( - unsafe { CStr::from_ptr(fn_entry.fname) }.to_str().expect("Should be a valid method name"), - fn_entry - ) - ).collect(); - let mut errors: Vec = vec![]; - for &(name, expect_args, expect_flags) in expected_methods { - let index_opt = methods_and_names - .iter() - .position(|&(fn_name, _)| fn_name == name); - let Some(index) = index_opt else { - errors.push(format!("Missing registration of method {name}")); - continue; - }; - let (_, fn_entry) = methods_and_names.remove(index); - if fn_entry.flags != expect_flags { - errors.push(format!( - "Registration of method {name} has wrong flags, expected {expect_flags}, found {}", - fn_entry.flags - )); - } - if fn_entry.arg_info == expect_args.as_ptr() { - continue; - } - // Rust global `const` values are *not* guaranteed to refer to the same - // location in memory because they might be inlined; compare the actual values - if fn_entry.num_args + 1 != expect_args.len().try_into().expect("Valid as a usize") { - errors.push( - format!("Registration of method {name} has wrong arg info, expected {} args, found {}", - expect_args.len() - 1, fn_entry.num_args) - ); - continue; - } - let fn_entry_arginfo = unsafe { - std::slice::from_raw_parts( - fn_entry.arg_info, - (fn_entry.num_args + 1).try_into().expect("Valid as a usize"), - ) - }; - for (idx, (expected, found)) in expect_args.iter().zip(fn_entry_arginfo).enumerate() { - errors.extend(verify_arg(idx, name, expected, found)); - } - } - for (name, _) in methods_and_names { - errors.push(format!("Unexpected registration of method {name}")); - } - let expected: &[String] = &[]; - assert_eq!(expected, errors, "Registration of {debug_name} should be correct"); -} /// Argument information for `test1()`. pub(crate) const ARGINFO_TEST1: [zend_internal_arg_info; 1] = [ ZEND_ARG_INFO_FOR_FUNCTION_WITH_RETURN_TYPE_MASK!(0, 0, MAY_BE_VOID, 0), diff --git a/crates/stub-parser/tests/verification-cases/no_methods.expected b/crates/stub-parser/tests/verification-cases/no_methods.expected index e045237..4c165de 100644 --- a/crates/stub-parser/tests/verification-cases/no_methods.expected +++ b/crates/stub-parser/tests/verification-cases/no_methods.expected @@ -1,213 +1,7 @@ use rsphp_macros_c::*; use rsphp_macros_rs::*; use rsphp_sys::*; -#[cfg(debug_assertions)] -use std::ffi::{c_char, CStr}; -/// Check if two type lists match. -#[cfg(debug_assertions)] -fn type_lists_match(expected: &zend_type_list, actual: &zend_type_list) -> bool { - if expected.num_types != actual.num_types { - return false; - } - let expected_types: &[zend_type] = unsafe { - std::slice::from_raw_parts( - expected.types.as_ptr(), - expected.num_types.try_into().expect("Valid as a usize"), - ) - }; - let actual_types: &[zend_type] = unsafe { - std::slice::from_raw_parts(actual.types.as_ptr(), actual.num_types.try_into().expect("Valid as a usize")) - }; - expected_types.iter().zip(actual_types).all(|(e, a)| types_match(e, a).is_none()) -} - -/// Check if two types match, returning an error message with some details if -/// they do not. -#[expect(clippy::unwrap_in_result, reason = "Failures are Some(s) rather than None, and startup code should panic")] -#[cfg(debug_assertions)] -fn types_match(expected: &zend_type, actual: &zend_type) -> Option { - if expected.type_mask != actual.type_mask { - return Some(format!("Expected type mask 0x{:x}, found 0x{:x}", expected.type_mask, actual.type_mask)); - } - if expected.ptr.is_null() { - if actual.ptr.is_null() { - return None; - } - return Some("Expected no pointer to be set in actual type".to_owned()); - } - if actual.ptr.is_null() { - return Some("Expected a pointer to be set in the actual type".to_owned()); - } - // Compare the values depending on the flags - if expected.type_mask & _ZEND_TYPE_LITERAL_NAME_BIT != 0 { - // CString pointers - let expected_name = unsafe { CStr::from_ptr(expected.ptr.cast::()) }; - let actual_name = unsafe { CStr::from_ptr(actual.ptr.cast::()) }; - if expected_name == actual_name { - return None; - } - return Some(format!("Expected literal name {expected_name:?}, found {actual_name:?}")); - } - if expected.type_mask & _ZEND_TYPE_NAME_BIT != 0 { - // zend_string pointers, compare them by comparing the lengths and values - let expected_name: &zend_string = unsafe { expected.ptr.cast::().as_ref().expect("Valid pointer") }; - let actual_name: &zend_string = unsafe { actual.ptr.cast::().as_ref().expect("Valid pointer") }; - if expected_name.len != actual_name.len { - return Some(format!("Expected name of length {}, found name of length {}", expected_name.len, actual_name.len)); - } - let expected_slice: &[c_char] = unsafe { std::slice::from_raw_parts(expected_name.val.as_ptr(), expected_name.len) }; - let actual_slice: &[c_char] = unsafe { std::slice::from_raw_parts(actual_name.val.as_ptr(), actual_name.len) }; - if expected_slice == actual_slice { - return None; - } - return Some(format!("Expected name {expected_slice:?}, found {actual_slice:?}")); - } - if expected.type_mask & _ZEND_TYPE_LIST_BIT != 0 { - // zend_type_list pointers - let expected_list: &zend_type_list = unsafe { expected.ptr.cast::().as_ref().expect("Valid pointer") }; - let acutal_list: &zend_type_list = unsafe { actual.ptr.cast::().as_ref().expect("Valid pointer") }; - if type_lists_match(expected_list, acutal_list) { - return None; - } - return Some("Type lists differ".to_owned()); - } - panic!("Unhandled zend types, unknown mask type: {}", expected.type_mask); -} - -/// Verify a function argument information matches the expected information, -/// returning an error of errors. -#[cfg(debug_assertions)] -fn verify_arg( - idx: usize, - method: &str, - expected: &zend_internal_arg_info, - actual: &zend_internal_arg_info, -) -> Vec { - let mut errors: Vec = vec![]; - if idx == 0 { - // Dummy entry at the start with information for the overall function - assert!(expected.default_value.is_null(), "{method}: expected arg info for function registration cannot have a default value"); - if !actual.default_value.is_null() { - errors.push(format!("{method}: Arg info for function registration has an unexpected default value")); - } - // `name` is abused as the number of required arguments - let expected_req_args = expected.name.addr(); - let actual_req_args = actual.name.addr(); - if expected_req_args != actual_req_args { - errors.push(format!("{method}: expected {expected_req_args} required arguments, found {actual_req_args}")); - } - if let Some(error) = types_match(&expected.type_, &actual.type_) { - errors.push(format!("{method}: return types differ: {error}")); - } - return errors; - } - let expected_name = unsafe { CStr::from_ptr(expected.name) }; - if actual.name.is_null() { - errors.push(format!("{method}: argument {} is missing a name", idx - 1)); - } else { - let actual_name = unsafe { CStr::from_ptr(actual.name) }; - if expected_name != actual_name { - errors.push(format!("{method}: argument {} expected to be named {expected_name:?}, actually named {actual_name:?}", idx - 1)); - } - } - if let Some(error) = types_match(&expected.type_, &actual.type_) { - errors.push(format!("{method}: argument {} type does not match expected: {error}", idx - 1)); - } - match (expected.default_value.is_null(), actual.default_value.is_null()) { - (true, true) => (), - (true, false) => { - let actual_default = unsafe { CStr::from_ptr(actual.default_value) }; - errors.push(format!("{method}: argument {} has unexpected default: {actual_default:?}", idx - 1)); - } - (false, true) => { - let expected_default = unsafe { CStr::from_ptr(expected.default_value) }; - errors.push(format!("{method}: argument {} missing expected default: {expected_default:?}", idx - 1)); - } - (false, false) => { - let actual_default = unsafe { CStr::from_ptr(actual.default_value) }; - let expected_default = unsafe { CStr::from_ptr(expected.default_value) }; - if actual_default != expected_default { - errors.push(format!( - "{method}: argument {} expected to have default {expected_default:?}, found {actual_default:?}", - idx - 1 - )); - } - } - } - errors -} - -/// Verify the registration of a function or method. -#[cfg(debug_assertions)] -fn verify_registration( - debug_name: &str, - expected_methods: &[(&str, &[zend_internal_arg_info], u32)], - actual_methods: &[zend_function_entry], -) { - let mut remaining_methods = actual_methods.to_vec(); - // Must end with the results of ZEND_FE_END - // So that we can use unwrapping below, do these assertions immediately - let last = remaining_methods.pop().unwrap_or_else(|| panic!("{debug_name} registration should end with ZEND_FE_END")); - assert!(last.fname.is_null(), "{debug_name} registration should end with ZEND_FE_END"); - assert!(last.handler.is_none(), "{debug_name} registration should end with ZEND_FE_END"); - assert!(last.arg_info.is_null(), "{debug_name} registration should end with ZEND_FE_END"); - assert_eq!(0, last.num_args, "{debug_name} registration should end with ZEND_FE_END"); - assert_eq!(0, last.flags, "{debug_name} registration should end with ZEND_FE_END"); - assert!(last.frameless_function_infos.is_null(), "{debug_name} registration should end with ZEND_FE_END"); - assert!(last.doc_comment.is_null(), "{debug_name} registration should end with ZEND_FE_END"); - - // So that we don't need to keep converting from char pointers - let mut methods_and_names: Vec<_> = remaining_methods.into_iter().map( - |fn_entry| ( - unsafe { CStr::from_ptr(fn_entry.fname) }.to_str().expect("Should be a valid method name"), - fn_entry - ) - ).collect(); - let mut errors: Vec = vec![]; - for &(name, expect_args, expect_flags) in expected_methods { - let index_opt = methods_and_names - .iter() - .position(|&(fn_name, _)| fn_name == name); - let Some(index) = index_opt else { - errors.push(format!("Missing registration of method {name}")); - continue; - }; - let (_, fn_entry) = methods_and_names.remove(index); - if fn_entry.flags != expect_flags { - errors.push(format!( - "Registration of method {name} has wrong flags, expected {expect_flags}, found {}", - fn_entry.flags - )); - } - if fn_entry.arg_info == expect_args.as_ptr() { - continue; - } - // Rust global `const` values are *not* guaranteed to refer to the same - // location in memory because they might be inlined; compare the actual values - if fn_entry.num_args + 1 != expect_args.len().try_into().expect("Valid as a usize") { - errors.push( - format!("Registration of method {name} has wrong arg info, expected {} args, found {}", - expect_args.len() - 1, fn_entry.num_args) - ); - continue; - } - let fn_entry_arginfo = unsafe { - std::slice::from_raw_parts( - fn_entry.arg_info, - (fn_entry.num_args + 1).try_into().expect("Valid as a usize"), - ) - }; - for (idx, (expected, found)) in expect_args.iter().zip(fn_entry_arginfo).enumerate() { - errors.extend(verify_arg(idx, name, expected, found)); - } - } - for (name, _) in methods_and_names { - errors.push(format!("Unexpected registration of method {name}")); - } - let expected: &[String] = &[]; - assert_eq!(expected, errors, "Registration of {debug_name} should be correct"); -} /// Module for `Example` class. pub(crate) mod Example { use rsphp_macros_c::*; From bcdff5170fde54ac4021a02f5126e18a30b668df Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Mon, 12 Oct 2026 13:12:02 -0700 Subject: [PATCH 04/12] Stub parser: initial implementation of `use` tracking This is ugly, and will be cleaned up momentarily, but it works --- crates/stub-parser/src/builder.rs | 700 +++++++++++++----- crates/stub-parser/src/types.rs | 194 +++-- crates/stub-parser/src/verification.rs | 23 + .../cases/attribute_registration.expected | 9 +- .../tests/cases/backed_enum.expected | 7 +- .../cases/class_constant_unknown.expected | 7 +- .../tests/cases/class_constants.expected | 21 +- .../tests/cases/class_extends.expected | 11 +- .../tests/cases/class_flags.expected | 15 +- .../tests/cases/class_implements.expected | 11 +- .../tests/cases/class_registration.expected | 7 +- .../cases/default_unknown_param.expected | 9 +- crates/stub-parser/tests/cases/enum.expected | 6 +- .../tests/cases/enum_types.expected | 14 +- .../tests/cases/functions.expected | 6 +- .../tests/cases/interface_extends.expected | 7 +- .../cases/interface_registration.expected | 12 +- .../tests/cases/method_attribs.expected | 9 +- .../stub-parser/tests/cases/methods.expected | 9 +- .../tests/cases/namespaced_bodies.expected | 17 +- .../tests/cases/namespaced_default.expected | 9 +- .../tests/cases/namespaced_semi.expected | 11 +- .../tests/cases/namespaced_types.expected | 12 +- .../tests/cases/nullable_property.expected | 7 +- .../tests/cases/optional_object.expected | 17 +- .../tests/cases/parameter_types.expected | 6 +- .../tests/cases/property_defaults.expected | 8 +- .../tests/cases/tentative_return.expected | 9 +- .../tests/cases/typed_properties.expected | 17 +- .../tests/cases/variadic_param.expected | 9 +- crates/stub-parser/tests/compile_test.rs | 2 +- .../verification-cases/functions.expected | 5 +- .../interface_registration.expected | 16 +- .../method_attribs.expected | 13 +- .../tests/verification-cases/methods.expected | 13 +- .../namespaced_semi.expected | 15 +- .../verification-cases/no_methods.expected | 14 +- 37 files changed, 797 insertions(+), 480 deletions(-) diff --git a/crates/stub-parser/src/builder.rs b/crates/stub-parser/src/builder.rs index 39068a0..90d35b5 100644 --- a/crates/stub-parser/src/builder.rs +++ b/crates/stub-parser/src/builder.rs @@ -1,6 +1,6 @@ use crate::VerificationMode; use crate::types::ArgInfoTypeRepr; -use crate::verification::VerificationData; +use crate::verification::{VERIFICATION_RSPHP_SYS_IMPORTS, VerificationData}; use php_ast::ast::{BuiltinType, Visibility}; use php_ast::owned::{ Attribute, ClassConstDecl, ClassDecl, ClassMemberKind, Comment, EnumCase, EnumDecl, @@ -10,6 +10,7 @@ use php_ast::owned::{ use php_ast::span::Span; use regex::Regex; use std::assert_matches; +use std::collections::HashSet; use std::fmt::Write as _; /// Wrapper around `EnumDecl`, `ClassDecl`, or `InterfaceDecl` to present a @@ -62,6 +63,10 @@ impl TryFrom for ClassLikeMember { } } +#[expect( + unused_results, + reason = "HashSet used to prevent duplication, just need to include the import" +)] impl ClassLikeType { /// Name of the class/interface/enum. fn name(&self) -> &str { @@ -140,11 +145,12 @@ impl ClassLikeType { /// interfaces that it should, with the assumption that the interfaces /// are available under the names from [`Self::interface_variables()`], and the /// overall class entry is available under the name `class_entry`. - fn interfaces_applied_code(&self) -> String { + fn interfaces_applied_code(&self, scope: &mut ImportsUsed) -> String { let interface_ptrs = self.interface_variables(); if interface_ptrs.is_empty() { String::new() } else { + scope.import_sys.insert("zend_class_implements".to_owned()); format!( "\n{space}unsafe {{\ \n{space}{space}zend_class_implements(\ @@ -166,6 +172,7 @@ impl ClassLikeType { #[expect(clippy::too_many_lines, reason = "It's a complicated function")] fn init_and_register_code( &self, + scope: &mut ImportsUsed, namespace: Option<&String>, method_count: usize, program: &str, @@ -175,15 +182,21 @@ impl ClassLikeType { Some(ns) => format!("{ns}\\\\"), None => String::new(), }; - let add_interfaces = self.interfaces_applied_code(); - let init_macro = match (namespace, method_count) { - (Some(ns), 0) => format!("INIT_NS_CLASS_ENTRY!(ce, \"{ns}\", \"{name}\", NULL);"), - (Some(ns), _) => { - format!("INIT_NS_CLASS_ENTRY!(ce, \"{ns}\", \"{name}\", methods);") - } - (None, 0) => format!("INIT_CLASS_ENTRY!(ce, \"{name}\", NULL);"), - (None, _) => format!("INIT_CLASS_ENTRY!(ce, \"{name}\", methods);"), + let add_interfaces = self.interfaces_applied_code(scope); + let (macro_name, macro_args) = match (namespace, method_count) { + (Some(ns), 0) => ( + "INIT_NS_CLASS_ENTRY", + format!("(ce, \"{ns}\", \"{name}\", NULL)"), + ), + (Some(ns), _) => ( + "INIT_NS_CLASS_ENTRY", + format!("(ce, \"{ns}\", \"{name}\", methods)"), + ), + (None, 0) => ("INIT_CLASS_ENTRY", format!("(ce, \"{name}\", NULL)")), + (None, _) => ("INIT_CLASS_ENTRY", format!("(ce, \"{name}\", methods)")), }; + let init_macro = format!("{macro_name}!{macro_args};"); + match self { ClassLikeType::Class(class) => { let extends_param = self @@ -192,15 +205,22 @@ impl ClassLikeType { let mut flags = vec![]; if class.modifiers.is_final { flags.push("ZEND_ACC_FINAL"); + scope.import_sys.insert("ZEND_ACC_FINAL".to_owned()); } match class.doc_comment.as_ref() { None => (), Some(comment) => { if comment.text.contains("@strict-properties") { flags.push("ZEND_ACC_NO_DYNAMIC_PROPERTIES"); + scope + .import_sys + .insert("ZEND_ACC_NO_DYNAMIC_PROPERTIES".to_owned()); } if comment.text.contains("@not-serializable") { flags.push("ZEND_ACC_NOT_SERIALIZABLE"); + scope + .import_sys + .insert("ZEND_ACC_NOT_SERIALIZABLE".to_owned()); } } } @@ -209,6 +229,11 @@ impl ClassLikeType { } else { flags.join("|") }; + scope.import_sys.insert("zend_class_entry".to_owned()); + scope + .import_sys + .insert("zend_register_internal_class_with_flags".to_owned()); + scope.import_macros_c.insert(macro_name.to_owned()); Ok(format!( "\n{space}let mut ce: zend_class_entry = unsafe {{ std::mem::zeroed() }};\ \n{space}{init_macro}\ @@ -249,6 +274,11 @@ impl ClassLikeType { return Err(format!("Invalid backing type: {backing_type:?}")); } }; + scope.import_sys.insert("zend_class_entry".to_owned()); + scope.import_sys.insert(backing_type_code.to_owned()); + scope + .import_sys + .insert("zend_register_internal_enum".to_owned()); Ok(format!( "\n{space}let class_entry: *mut zend_class_entry = unsafe {{\ \n{space}{space}zend_register_internal_enum(\ @@ -261,17 +291,24 @@ impl ClassLikeType { space = " ", )) } - ClassLikeType::Interface(_) => Ok(format!( - "\n{space}let mut ce: zend_class_entry = unsafe {{ std::mem::zeroed() }};\ - \n{space}{init_macro}\ - \n{space}let class_entry: *mut zend_class_entry = unsafe {{\ - \n{space}{space}zend_register_internal_interface(\ - \n{space}{space}{space}std::ptr::from_mut::(&mut ce),\ - \n{space}{space})\ - \n{space}}};\ - {add_interfaces}", - space = " ", - )), + ClassLikeType::Interface(_) => { + scope.import_sys.insert("zend_class_entry".to_owned()); + scope + .import_sys + .insert("zend_register_internal_interface".to_owned()); + scope.import_macros_c.insert(macro_name.to_owned()); + Ok(format!( + "\n{space}let mut ce: zend_class_entry = unsafe {{ std::mem::zeroed() }};\ + \n{space}{init_macro}\ + \n{space}let class_entry: *mut zend_class_entry = unsafe {{\ + \n{space}{space}zend_register_internal_interface(\ + \n{space}{space}{space}std::ptr::from_mut::(&mut ce),\ + \n{space}{space})\ + \n{space}}};\ + {add_interfaces}", + space = " ", + )) + } } } /// The kind of class-like-type this is, one of "enum", "interface", or @@ -293,6 +330,58 @@ impl ClassLikeType { } } +/// Tracker for what imports need to be added to a given scope. +#[derive(Default)] +#[expect( + clippy::field_scoped_visibility_modifiers, + clippy::struct_field_names, + reason = "To be refactored" +)] +pub(crate) struct ImportsUsed { + /// Imports needed from `rsphp_sys`. + pub(crate) import_sys: HashSet, + /// Imports needed from `rsphp_macros_c`. + pub(crate) import_macros_c: HashSet, + /// Imports needed from `rsphp_macros_rs`. + pub(crate) import_macros_rs: HashSet, +} + +impl ImportsUsed { + /// Create a new empty set of imports. + fn new() -> Self { + Self { + import_sys: HashSet::new(), + import_macros_c: HashSet::new(), + import_macros_rs: HashSet::new(), + } + } + + /// Format all imports. + fn as_uses(&self) -> String { + vec![ + Self::as_use_single(&self.import_macros_c, "rsphp_macros_c"), + Self::as_use_single(&self.import_macros_rs, "rsphp_macros_rs"), + Self::as_use_single(&self.import_sys, "rsphp_sys"), + ] + .into_iter() + .flatten() + .collect::() + } + + /// Format imports for a single module. + fn as_use_single(used: &HashSet, module: &str) -> Option { + if used.is_empty() { + return None; + } + let mut used_names: Vec = used.clone().into_iter().collect(); + if used_names.len() == 1 { + return Some(format!("use {module}::{};\n", used_names[0])); + } + used_names.sort(); + Some(format!("use {module}::{{{}}};\n", used_names.join(", "))) + } +} + /// Stateful tool for building up argument information for a program. pub(crate) struct ArgInfoBuilder<'program> { /// The program text. @@ -307,6 +396,8 @@ pub(crate) struct ArgInfoBuilder<'program> { /// then there is nothing to verify and the verification code doesn't need /// to be added. needs_verification: bool, + /// File-level imports used. + file_uses: ImportsUsed, } impl<'program> ArgInfoBuilder<'program> { @@ -319,6 +410,7 @@ impl<'program> ArgInfoBuilder<'program> { namespace: None, verification, needs_verification: false, + file_uses: ImportsUsed::new(), } } } @@ -328,6 +420,11 @@ impl<'program> ArgInfoBuilder<'program> { clippy::needless_pass_by_ref_mut, reason = "Non-wildcard imports not set up yet" )] +#[expect( + unused_results, + reason = "HashSet used to prevent duplication, just need to include the import" +)] +#[expect(clippy::too_many_lines, reason = "Many complicated functions")] impl ArgInfoBuilder<'_> { /// Get a snippet of the raw code in the program at the location specified /// in the span. @@ -382,14 +479,13 @@ impl ArgInfoBuilder<'_> { Ok(()) } /// Generate code for a function parameter. - fn param(&mut self, param: &Param) -> Result { + fn param(&self, scope: &mut ImportsUsed, param: &Param) -> Result { let by_ref = u8::from(param.by_ref); - let param_type_info = param.type_hint.as_ref().map(|type_hint| { - let th = ArgInfoTypeRepr::try_from(type_hint.kind.clone(), self.namespace.as_ref()) + let param_type_info_opt = param.type_hint.as_ref().map(|type_hint| { + ArgInfoTypeRepr::try_from(type_hint.kind.clone(), self.namespace.as_ref()) .expect("Type representation should be valid") .typehint(self.namespace.as_ref()) - .expect("Type representation should work for parameters"); - (th.class_info, th.mask_info, th.return_info, th.is_nullable) + .expect("Type representation should work for parameters") }); let name = param .name @@ -412,62 +508,99 @@ impl ArgInfoBuilder<'_> { default = None; } let maybe_variadic = if param.variadic { "VARIADIC_" } else { "" }; - #[expect(clippy::uninlined_format_args, reason = "Clearer to not inline")] - let macro_result = match (param_type_info, default) { - (None, None) => format!( - "ZEND_ARG_{v}INFO!({by_ref}, \"{name}\")", - v = maybe_variadic + let Some(param_type_info) = param_type_info_opt else { + let (macro_name, macro_args) = match default { + None => ( + format!("ZEND_ARG_{maybe_variadic}INFO"), + format!("({by_ref}, \"{name}\")"), + ), + Some(dflt) => ( + "ZEND_ARG_INFO_WITH_DEFAULT_VALUE".to_owned(), + format!("({by_ref}, \"{name}\", \"{dflt}\")"), + ), + }; + scope.import_macros_c.insert(macro_name.clone()); + return Ok(format!(" {macro_name}!{macro_args},\n")); + }; + let (macro_name, macro_args) = match ( + ( + param_type_info.class_info, + param_type_info.mask_info, + param_type_info.return_info, + param_type_info.is_nullable, ), - (None, Some(dflt)) => { - format!("ZEND_ARG_INFO_WITH_DEFAULT_VALUE!({by_ref}, \"{name}\", \"{dflt}\")") - } - (Some((None, None, _, _)), _) => { + default, + ) { + ((None, None, _, _), _) => { return Err(format!("No class or type mask for {param:?}")); } - (Some((None, Some(param_mask), param_info, nullable)), None) => { + ((None, Some(param_mask), param_info, nullable), None) => { if param_mask.contains('|') { - format!( - "ZEND_ARG_TYPE_MASK!({by_ref}, \"{name}\", {param_mask}, std::ptr::null())", + scope.import_sys.extend(param_type_info.mask_uses); + ( + "ZEND_ARG_TYPE_MASK".to_owned(), + format!("({by_ref}, \"{name}\", {param_mask}, std::ptr::null())"), ) } else { - format!( - "ZEND_ARG_{v}TYPE_INFO!({by_ref}, \"{name}\", {}, {})", - param_info.expect("Should be set"), - u8::from(nullable), - v = maybe_variadic, + scope.import_sys.extend(param_type_info.return_uses); + ( + format!("ZEND_ARG_{maybe_variadic}TYPE_INFO"), + format!( + "({by_ref}, \"{name}\", {}, {})", + param_info.expect("Should be set"), + u8::from(nullable), + ), ) } } - (Some((Some(param_class), None, None, nullable)), None) => format!( - "ZEND_ARG_{v}OBJ_INFO!({by_ref}, \"{name}\", \"{param_class}\", {})", - u8::from(nullable), - v = maybe_variadic, - ), - (Some((Some(param_class), Some(param_mask), _, _)), dflt) => format!( - "ZEND_ARG_{v}OBJ_TYPE_MASK!({by_ref}, \"{name}\", \"{param_class}\", {param_mask}, {})", - match dflt { - Some(default_repr) => default_repr, - None => "NULL".to_owned(), - }, - v = maybe_variadic, - ), - (Some((None, _, Some(param_info), nullable)), Some(dflt)) => format!( - "ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE!({by_ref}, \"{name}\", {param_info}, {}, \"{dflt}\")", - u8::from(nullable), + ((Some(param_class), None, None, nullable), None) => ( + format!("ZEND_ARG_{maybe_variadic}OBJ_INFO"), + format!( + "({by_ref}, \"{name}\", \"{param_class}\", {})", + u8::from(nullable), + ), ), - (Some((Some(param_class), None, None, nullable)), Some(dflt)) => format!( - "ZEND_ARG_{v}OBJ_INFO_WITH_DEFAULT_VALUE!({by_ref}, \"{name}\", \"{param_class}\", {}, \"{dflt}\")", - u8::from(nullable), - v = maybe_variadic, + ((Some(param_class), Some(param_mask), _, _), dflt) => { + scope.import_sys.extend(param_type_info.mask_uses); + ( + format!("ZEND_ARG_{maybe_variadic}OBJ_TYPE_MASK"), + format!( + "({by_ref}, \"{name}\", \"{param_class}\", {param_mask}, {})", + match dflt { + Some(default_repr) => default_repr, + None => "NULL".to_owned(), + }, + ), + ) + } + ((None, _, Some(param_info), nullable), Some(dflt)) => { + scope.import_sys.extend(param_type_info.return_uses); + ( + "ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE".to_owned(), + format!( + "({by_ref}, \"{name}\", {param_info}, {}, \"{dflt}\")", + u8::from(nullable), + ), + ) + } + ((Some(param_class), None, None, nullable), Some(dflt)) if !param.variadic => ( + "ZEND_ARG_OBJ_INFO_WITH_DEFAULT_VALUE".to_owned(), + format!( + "({by_ref}, \"{name}\", \"{param_class}\", {}, \"{dflt}\")", + u8::from(nullable), + ), ), _ => return Err(format!("Unhandled parameter: {param:?}")), }; - Ok(format!(" {macro_result},\n")) + scope.import_macros_c.insert(macro_name.clone()); + Ok(format!(" {macro_name}!{macro_args},\n")) } /// Generate code for a global function or class method. + #[expect(clippy::too_many_arguments, reason = "To be refactored")] fn build_function( - &mut self, + &self, + scope: &mut ImportsUsed, params: &[Param], comment_opt: Option<&Comment>, name_constant: &str, @@ -483,10 +616,14 @@ impl ArgInfoBuilder<'_> { } let param_code: Vec = params .iter() - .map(|parameter| self.param(parameter)) + .map(|parameter| self.param(scope, parameter)) .collect::, _>>()?; let return_reference_int: u8 = u8::from(return_reference); let Some(return_info) = return_type else { + scope.import_sys.insert("zend_internal_arg_info".to_owned()); + scope + .import_macros_rs + .insert("ZEND_ARG_INFO_FOR_FUNCTION".to_owned()); return Ok(format!( "/// Argument information for `{doc_name}()`.\ \npub(crate) const {name_constant}: [zend_internal_arg_info; {}] = [\ @@ -508,34 +645,57 @@ impl ArgInfoBuilder<'_> { }; let tentative_int = u8::from(tentative); #[expect(clippy::uninlined_format_args, reason = "Clearer to not inline")] - let fn_arginfo = match ( + let (fn_arginfo_macro, fn_arginfo_params) = match ( typehint.class_info, typehint.mask_info, typehint.return_info, ) { (None, None, None) => return Err(format!("No return info for {return_info:?}")), - (Some(class), None, None) => format!( - "ZEND_ARG_INFO_FOR_FUNCTION_WITH_RETURN_OBJ_INFO!({}, {}, \"{}\", {}, {})", - return_reference_int, required_params, class, ret_nullable, tentative_int, - ), - (None, _, Some(info)) if typehint.is_nullable => format!( - "ZEND_ARG_INFO_FOR_FUNCTION_WITH_RETURN_TYPE_INFO!({}, {}, {}, 1, {})", - return_reference_int, required_params, info, tentative_int, - ), - (None, Some(mask), _) => format!( - "ZEND_ARG_INFO_FOR_FUNCTION_WITH_RETURN_TYPE_MASK!({}, {}, {}, {})", - return_reference_int, required_params, mask, tentative_int, - ), - (Some(class), Some(mask), _) => format!( - "ZEND_ARG_INFO_FOR_FUNCTION_WITH_RETURN_OBJ_TYPE_INFO!({}, {}, \"{}\", {}, {})", - return_reference_int, required_params, class, mask, tentative_int, + (Some(class), None, None) => ( + "ZEND_ARG_INFO_FOR_FUNCTION_WITH_RETURN_OBJ_INFO", + format!( + "({}, {}, \"{}\", {}, {})", + return_reference_int, required_params, class, ret_nullable, tentative_int, + ), ), + (None, _, Some(info)) if typehint.is_nullable => { + scope.import_sys.extend(typehint.return_uses); + ( + "ZEND_ARG_INFO_FOR_FUNCTION_WITH_RETURN_TYPE_INFO", + format!( + "({}, {}, {}, 1, {})", + return_reference_int, required_params, info, tentative_int, + ), + ) + } + (None, Some(mask), _) => { + scope.import_sys.extend(typehint.mask_uses); + ( + "ZEND_ARG_INFO_FOR_FUNCTION_WITH_RETURN_TYPE_MASK", + format!( + "({}, {}, {}, {})", + return_reference_int, required_params, mask, tentative_int, + ), + ) + } + (Some(class), Some(mask), _) => { + scope.import_sys.extend(typehint.mask_uses); + ( + "ZEND_ARG_INFO_FOR_FUNCTION_WITH_RETURN_OBJ_TYPE_INFO", + format!( + "({}, {}, \"{}\", {}, {})", + return_reference_int, required_params, class, mask, tentative_int, + ), + ) + } _ => return Err(format!("Unhandled return info: {return_info:?}")), }; + scope.import_sys.insert("zend_internal_arg_info".to_owned()); + scope.import_macros_rs.insert(fn_arginfo_macro.to_owned()); Ok(format!( "/// Argument information for `{doc_name}()`.\ \npub(crate) const {name_constant}: [zend_internal_arg_info; {}] = [\ - \n{space}{fn_arginfo},\n\ + \n{space}{fn_arginfo_macro}!{fn_arginfo_params},\n\ {}];\n", param_code.len() + 1, param_code.join(""), @@ -554,101 +714,90 @@ impl ArgInfoBuilder<'_> { Some(ns) => format!("{ns}\\{fn_name}"), None => fn_name.to_string(), }; + // Can't borrow from self.file_uses as mutable while calling a function + let mut file_uses = std::mem::take(&mut self.file_uses); let fn_result = self.build_function( + &mut file_uses, &func.params, func.doc_comment.as_ref(), &format!("ARGINFO_{ns_segment}{}", fn_name.to_uppercase()), func.by_ref, func.return_type.as_ref(), &doc_name, - )?; - self.parts.push(fn_result); + ); + self.file_uses = file_uses; + self.parts.push(fn_result?); Ok(()) } /// Generate code for a class property. - #[expect(clippy::too_many_lines, reason = "It's a complicated function")] - fn build_property(&mut self, prop: &PropertyDecl) -> Result { + fn build_property( + &self, + scope: &mut ImportsUsed, + prop: &PropertyDecl, + ) -> Result { + scope.import_sys.insert("zval".to_owned()); + scope.import_sys.insert("zend_class_entry".to_owned()); let prop_name = prop.name.as_ref().ok_or("Property name missing")?; let prop_default_create = format!( "{space}let mut property_{name}_default_value: zval = unsafe {{ std::mem::zeroed() }};", name = prop_name, space = " " ); - let prop_default_init = match &prop.default { - None => format!( - "{space}unsafe {{ ZVAL_UNDEF!(std::ptr::from_mut::(&mut property_{name}_default_value)); }}", - name = prop_name, - space = " " - ), + // rsphp-macros-c macro to call, Option with an extra argument, and + // Option with extra code to put before the initialization + let (init_macro, init_arg, init_extra_code) = match &prop.default { + None => ("ZVAL_UNDEF", None, None), Some(Expr { kind: ExprKind::Null, span: _, - }) => format!( - "{space}unsafe {{ ZVAL_NULL!(std::ptr::from_mut::(&mut property_{name}_default_value)); }}", - name = prop_name, - space = " " - ), + }) => ("ZVAL_NULL", None, None), Some(Expr { kind: ExprKind::Bool(false), span: _, - }) => format!( - "{space}unsafe {{ ZVAL_FALSE!(std::ptr::from_mut::(&mut property_{name}_default_value)); }}", - name = prop_name, - space = " " - ), + }) => ("ZVAL_FALSE", None, None), Some(Expr { kind: ExprKind::Bool(true), span: _, - }) => format!( - "{space}unsafe {{ ZVAL_TRUE!(std::ptr::from_mut::(&mut property_{name}_default_value)); }}", - name = prop_name, - space = " " - ), + }) => ("ZVAL_TRUE", None, None), Some(Expr { kind: ExprKind::String(string), span: _, - }) if **string == *"" => format!( - "{space}unsafe {{ ZVAL_EMPTY_STRING!(std::ptr::from_mut::(&mut property_{name}_default_value)); }}", - name = prop_name, - space = " " - ), + }) if **string == *"" => ("ZVAL_EMPTY_STRING", None, None), Some(Expr { kind: ExprKind::Array(array), span: _, - }) if array.is_empty() => format!( - "{space}unsafe {{ ZVAL_EMPTY_ARRAY!(std::ptr::from_mut::(&mut property_{name}_default_value)); }}", - name = prop_name, - space = " " - ), + }) if array.is_empty() => ("ZVAL_EMPTY_ARRAY", None, None), Some(Expr { kind: ExprKind::Int(num), span: _, - }) => format!( - "{space}unsafe {{ ZVAL_LONG!(std::ptr::from_mut::(&mut property_{name}_default_value), {num}); }}", - name = prop_name, - space = " " - ), + }) => ("ZVAL_LONG", Some(num.to_string()), None), Some(Expr { kind: ExprKind::Identifier(ident), span: _, - }) if **ident == *"E_ERROR" => format!( - "{space}unsafe {{ ZVAL_LONG!(std::ptr::from_mut::(&mut property_{name}_default_value), E_ERROR); }}", - name = prop_name, - space = " " - ), + }) if **ident == *"E_ERROR" => { + scope.import_sys.insert("E_ERROR".to_owned()); + ("ZVAL_LONG", Some("E_ERROR".to_owned()), None) + } Some(Expr { kind: ExprKind::String(string), span: _, - }) => format!( - "{space}let property_{name}_default_str = unsafe {{ zend_string_init(c\"{s}\".as_ptr(), \"{s}\".len(), true) }};\ - \n{space}unsafe {{ ZVAL_INTERNED_STR!(std::ptr::from_mut::(&mut property_{name}_default_value), property_{name}_default_str); }}", - name = prop_name, - space = " ", - s = string, - ), + }) => { + scope.import_sys.insert("zend_string_init".to_owned()); + ( + "ZVAL_INTERNED_STR", + Some(format!("property_{prop_name}_default_str")), + Some(format!( + "{space}let property_{name}_default_str = unsafe {{ zend_string_init(c\"{s}\".as_ptr(), \"{s}\".len(), true) }};\n", + name = prop_name, + space = " ", + s = string + )), + ) + } _ => return Err(format!("Unhandled property default: {prop:?}")), }; + scope.import_macros_c.insert(init_macro.to_owned()); let prop_name_init = format!( "{space}let property_{name}_name: *mut zend_string = unsafe {{\ \n{space}{space}zend_string_init(\ @@ -660,24 +809,29 @@ impl ArgInfoBuilder<'_> { name = prop_name, space = " " ); + scope.import_sys.insert("zend_string".to_owned()); + scope.import_sys.insert("zend_string_init".to_owned()); let vis = match prop.visibility { None | Some(Visibility::Public) => "ZEND_ACC_PUBLIC", Some(Visibility::Protected) => "ZEND_ACC_PROTECTED", Some(Visibility::Private) => "ZEND_ACC_PRIVATE", }; + scope.import_sys.insert(vis.to_owned()); let (type_declaration, type_code) = match prop.type_hint.as_ref() { Some(type_hint) => { ArgInfoTypeRepr::try_from(type_hint.kind.clone(), self.namespace.as_ref())? - .property_type_code(self.namespace.as_ref(), prop_name)? + .property_type_code(scope, self.namespace.as_ref(), prop_name)? } None => (None, "ZEND_TYPE_INIT_NONE!(0)".to_owned()), }; + let ptr_for_default = + format!("std::ptr::from_mut::(&mut property_{prop_name}_default_value)"); let prop_decl = format!( "{space}unsafe {{\ \n{space}{space}zend_declare_typed_property(\ \n{space}{space}{space}class_entry,\ \n{space}{space}{space}property_{name}_name,\ - \n{space}{space}{space}std::ptr::from_mut::(&mut property_{name}_default_value),\ + \n{space}{space}{space}{ptr_for_default},\ \n{space}{space}{space}{vis} as i32,\ \n{space}{space}{space}std::ptr::null_mut(),\ \n{space}{space}{space}{typemask},\ @@ -694,7 +848,22 @@ impl ArgInfoBuilder<'_> { name = prop_name, space = " " ); - let mut parts = vec![prop_default_create, prop_default_init, prop_name_init]; + scope + .import_sys + .insert("zend_declare_typed_property".to_owned()); + scope.import_sys.insert("zend_string_release".to_owned()); + let mut parts = vec![prop_default_create]; + match init_extra_code { + None => (), + Some(code) => parts.push(code), + } + parts.push(format!( + "{space}unsafe {{ {init_macro}!({ptr_for_default}{sep}{extra_arg}); }}", + space = " ", + sep = if init_arg.is_some() { ", " } else { "" }, + extra_arg = init_arg.unwrap_or_default() + )); + parts.push(prop_name_init); match type_declaration { None => (), Some(code) => parts.push(code), @@ -705,8 +874,8 @@ impl ArgInfoBuilder<'_> { } /// Generate code for a class, enum, or interface declaration. - #[expect(clippy::too_many_lines, reason = "It's a complicated function")] fn class_like(&mut self, wrapped: &ClassLikeType) -> Result<(), String> { + let mut scope = ImportsUsed::new(); let class_name = wrapped.name(); let mut properties = vec![]; let mut constants = vec![]; @@ -732,6 +901,7 @@ impl ArgInfoBuilder<'_> { let fn_name = method.name.as_ref().expect("No errors"); let name_constant = format!("ARGINFO_{}", fn_name.to_uppercase()); module_parts.push(self.build_function( + &mut scope, &method.params, method.doc_comment.as_ref(), &name_constant, @@ -749,6 +919,7 @@ impl ArgInfoBuilder<'_> { ); for attrib in &method.attributes { attribute_setup.push(self.build_method_attribute( + &mut scope, fn_name, attrib, &mut verification, @@ -758,13 +929,17 @@ impl ArgInfoBuilder<'_> { self.needs_verification = true; } ClassLikeMember::Property(property) => { - properties.push(self.build_property(&property)?); + properties.push(self.build_property(&mut scope, &property)?); } ClassLikeMember::ClassConst(class_const) => { - constants.push(self.build_constant(&class_const)?); + constants.push(self.build_constant(&mut scope, &class_const)?); } ClassLikeMember::EnumCase(enum_case) => { - enum_cases.push(self.build_case(&enum_case, enum_backing.as_ref())?); + enum_cases.push(self.build_case( + &mut scope, + &enum_case, + enum_backing.as_ref(), + )?); } } } @@ -772,7 +947,7 @@ impl ArgInfoBuilder<'_> { let mut class_attributes = wrapped .attributes() .iter() - .map(|attribute| self.class_attribute(attribute)) + .map(|attribute| self.class_attribute(&mut scope, attribute)) .collect::, _>>()?; class_attributes.extend(attribute_setup); let class_attributes_str = if class_attributes.is_empty() { @@ -804,6 +979,7 @@ impl ArgInfoBuilder<'_> { registration_params.push(format!("{interface}: *mut zend_class_entry")); } if !method_verifications.is_empty() { + scope.import_sys.insert("zend_function_entry".to_owned()); registration_params.push("methods: &[zend_function_entry]".to_owned()); } // Registration function @@ -816,11 +992,8 @@ impl ArgInfoBuilder<'_> { .apply_to_methods(class_name, &method_verifications), }; let kind = wrapped.kind(); - let module_content = format!( - "use rsphp_macros_c::*;\n\ - {use_macros_rs}\ - use rsphp_sys::*;\n\ - {use_verify}\n\ + let mut module_content = format!( + "{use_verify}\n\ {module_parts}\n\ /// Register the `{doc_name}` {kind}.\ \npub(crate) fn register_{kind}({methods_param}) -> *mut zend_class_entry {{\ @@ -837,21 +1010,35 @@ impl ArgInfoBuilder<'_> { methods_param = registration_params.join(", "), module_parts = module_parts.join("\n"), init_and_register_code = wrapped.init_and_register_code( + &mut scope, self.namespace.as_ref(), method_verifications.len(), self.program )?, space = " ", - use_macros_rs = if method_verifications.is_empty() { - "" - } else { - "use rsphp_macros_rs::*;\n" - }, use_verify = self.verification.apply_to_import( "use super::verify_registration;\n", method_verifications.is_empty() ), ); + // Some extra imports might be conditional + let flags_in_verification = VerificationData::get_all_flags(&method_verifications); + match self.verification { + VerificationMode::Never => (), + VerificationMode::Always => { + scope.import_sys.extend(flags_in_verification); + } + VerificationMode::DebugAssert => { + let extra_flags_in_verification = &flags_in_verification - &scope.import_sys; + if let Some(extra_use) = + ImportsUsed::as_use_single(&extra_flags_in_verification, "rsphp_sys") + { + module_content = + format!("#[cfg(debug_assertions)]\n{extra_use}") + &module_content; + } + } + } + module_content = scope.as_uses() + &module_content; let module_content_indented = module_content .split('\n') .map(|line| { @@ -881,6 +1068,7 @@ impl ArgInfoBuilder<'_> { /// Generate code for registering an attribute on a class method. fn build_method_attribute( &self, + scope: &mut ImportsUsed, name: &str, attrib: &Attribute, verification: &mut VerificationData, @@ -900,15 +1088,16 @@ impl ArgInfoBuilder<'_> { return Err(format!("Argument name has multiple parts: {arg:?}")); } let arg_name = &arg_name_full.parts[0]; - let init = match arg_name.as_ref() { - "since" => format!( - "(*(*attrib).args.as_mut_ptr().add({index})).name = ZSTR_KNOWN!(_zend_known_string_id::ZEND_STR_SINCE);", - ), - "message" => format!( - "(*(*attrib).args.as_mut_ptr().add({index})).name = ZSTR_KNOWN!(_zend_known_string_id::ZEND_STR_MESSAGE);", - ), + let known_name = match arg_name.as_ref() { + "since" => "_zend_known_string_id::ZEND_STR_SINCE", + "message" => "_zend_known_string_id::ZEND_STR_MESSAGE", _ => return Err(format!("Unhandled attribute parameter name: {name}")), }; + scope.import_sys.insert("_zend_known_string_id".to_owned()); + let init = format!( + "(*(*attrib).args.as_mut_ptr().add({index})).name = ZSTR_KNOWN!({known_name});", + ); + scope.import_macros_c.insert("ZSTR_KNOWN".to_owned()); param_init.push(init); } match &arg @@ -922,10 +1111,13 @@ impl ArgInfoBuilder<'_> { "let arg_{index}_value = zend_string_init(c\"{string}\".as_ptr(), \ \"{string}\".len(), true);", ); + scope.import_sys.insert("zend_string_init".to_owned()); param_init.push(alloc); let param_use = format!( "ZVAL_STR!(std::ptr::from_mut::(&mut (*(*attrib).args.as_mut_ptr().add({index})).value), arg_{index}_value);", ); + scope.import_sys.insert("zval".to_owned()); + scope.import_macros_c.insert("ZVAL_STR".to_owned()); param_init.push(param_use); } _ => return Err(format!("Unhandled attribute value: {arg:#?}")), @@ -935,25 +1127,45 @@ impl ArgInfoBuilder<'_> { if attrib_name == "Deprecated" { verification.add_flag("ZEND_ACC_DEPRECATED"); } + #[expect(clippy::single_match_else, reason = "Clearer as a match")] let (name_alloc, name_use, name_free) = match attrib_name.as_str() { - "Deprecated" => ( - "\n assert_eq!(10, ZSTR_LEN!(ZSTR_KNOWN!(_zend_known_string_id::ZEND_STR_DEPRECATED_CAPITALIZED)));".to_owned(), - "ZSTR_KNOWN!(_zend_known_string_id::ZEND_STR_DEPRECATED_CAPITALIZED)".to_owned(), - String::new() - ), - _ => ( - format!( - "\n{space}{space}let attrib_name: *mut zend_string = (zend_string_init_interned.expect(\"zend_string_init_interned should be set\"))(\ - c\"{name}\".as_ptr(),\ - \"{name}\".len(),\ - true);", - space = " ", - name = attrib_name - ), - "attrib_name".to_owned(), - "\n zend_string_release_ex(attrib_name, true);".to_owned() - ), + "Deprecated" => { + scope.import_macros_c.insert("ZSTR_LEN".to_owned()); + scope.import_macros_c.insert("ZSTR_KNOWN".to_owned()); + scope.import_sys.insert("_zend_known_string_id".to_owned()); + ( + "\n assert_eq!(10, ZSTR_LEN!(ZSTR_KNOWN!(_zend_known_string_id::ZEND_STR_DEPRECATED_CAPITALIZED)));".to_owned(), + "ZSTR_KNOWN!(_zend_known_string_id::ZEND_STR_DEPRECATED_CAPITALIZED)".to_owned(), + String::new() + ) + } + _ => { + scope.import_sys.insert("zend_string".to_owned()); + scope + .import_sys + .insert("zend_string_init_interned".to_owned()); + scope.import_sys.insert("zend_string_release_ex".to_owned()); + ( + format!( + "\n{space}{space}let attrib_name: *mut zend_string = (zend_string_init_interned.expect(\"zend_string_init_interned should be set\"))(\ + c\"{name}\".as_ptr(),\ + \"{name}\".len(),\ + true);", + space = " ", + name = attrib_name + ), + "attrib_name".to_owned(), + "\n zend_string_release_ex(attrib_name, true);".to_owned(), + ) + } }; + scope.import_sys.insert("zend_function".to_owned()); + scope.import_sys.insert("zend_hash_str_find_ptr".to_owned()); + scope.import_sys.insert("HashTable".to_owned()); + scope.import_sys.insert("zend_attribute".to_owned()); + scope + .import_sys + .insert("zend_add_function_attribute".to_owned()); Ok(format!( "{space}unsafe {{\ \n{space}{space}let fn_ptr: *mut zend_function = zend_hash_str_find_ptr(\ @@ -980,7 +1192,11 @@ impl ArgInfoBuilder<'_> { } /// Generate code for registering a class constant. - fn build_constant(&self, constant: &ClassConstDecl) -> Result { + fn build_constant( + &self, + scope: &mut ImportsUsed, + constant: &ClassConstDecl, + ) -> Result { let constant_name = constant .name .as_ref() @@ -990,6 +1206,7 @@ impl ArgInfoBuilder<'_> { Some(Visibility::Protected) => "ZEND_ACC_PROTECTED", Some(Visibility::Private) => "ZEND_ACC_PRIVATE", }; + scope.import_sys.insert(vis.to_owned()); let type_repr = ArgInfoTypeRepr::try_from( constant.type_hint.as_ref().map_or_else( || { @@ -1014,10 +1231,16 @@ impl ArgInfoBuilder<'_> { self.namespace.as_ref(), )?; let (_type_declaration, type_code) = - type_repr.property_type_code(self.namespace.as_ref(), constant_name)?; + type_repr.property_type_code(scope, self.namespace.as_ref(), constant_name)?; let init_code = match &constant.value.kind { - ExprKind::Int(num) => format!("ZVAL_LONG!(&mut value, {num});"), - ExprKind::Null => "ZVAL_NULL!(&mut value);".to_owned(), + ExprKind::Int(num) => { + scope.import_macros_c.insert("ZVAL_LONG".to_owned()); + format!("ZVAL_LONG!(&mut value, {num});") + } + ExprKind::Null => { + scope.import_macros_c.insert("ZVAL_NULL".to_owned()); + "ZVAL_NULL!(&mut value);".to_owned() + } ExprKind::Identifier(ident) if *ident == "UNKNOWN".into() => { let cvalue_re = Regex::new(r"@cvalue (\S+)").map_err(|err| err.to_string())?; let mut cvalue_val = cvalue_re @@ -1034,17 +1257,24 @@ impl ArgInfoBuilder<'_> { .as_str() .to_owned(); // Might not be something defined in core - if !cvalue_val.starts_with("ZEND_") { + if cvalue_val.starts_with("ZEND_") || cvalue_val.starts_with("PHP_") { + scope.import_sys.insert(cvalue_val.clone()); + } else { cvalue_val = "crate::".to_owned() + &cvalue_val; } match type_repr { ArgInfoTypeRepr::Builtin(BuiltinType::Int) => { + scope.import_macros_c.insert("ZVAL_LONG".to_owned()); format!("ZVAL_LONG!(&mut value, {cvalue_val} as i64);") } ArgInfoTypeRepr::Builtin(BuiltinType::Float) => { + scope.import_macros_c.insert("ZVAL_DOUBLE".to_owned()); format!("ZVAL_DOUBLE!(&mut value, {cvalue_val} as f64);") } ArgInfoTypeRepr::Builtin(BuiltinType::String) => { + scope.import_sys.insert("zend_string".to_owned()); + scope.import_sys.insert("zend_string_init".to_owned()); + scope.import_macros_c.insert("ZVAL_STR".to_owned()); format!( "let value_str: *mut zend_string = zend_string_init(\ \n{space3}{cvalue_val}.as_ptr().cast::(),\ @@ -1064,6 +1294,15 @@ impl ArgInfoBuilder<'_> { } _ => return Err(format!("Unhandled constant initialization: {constant:#?}")), }; + scope.import_sys.insert("zval".to_owned()); + scope.import_sys.insert("zend_string".to_owned()); + scope + .import_sys + .insert("zend_string_init_interned".to_owned()); + scope + .import_sys + .insert("zend_declare_typed_class_constant".to_owned()); + scope.import_sys.insert("zend_string_release_ex".to_owned()); Ok(format!( "{space}unsafe {{\ \n{space}{space}let mut value: zval = std::mem::zeroed();\ @@ -1084,7 +1323,11 @@ impl ArgInfoBuilder<'_> { } /// Generate code for registering an attribute for the overall class/interface/enum. - fn class_attribute(&self, attrib: &Attribute) -> Result { + fn class_attribute( + &self, + scope: &mut ImportsUsed, + attrib: &Attribute, + ) -> Result { let attrib_name = attrib .name .parts @@ -1135,6 +1378,10 @@ impl ArgInfoBuilder<'_> { _ => Err(format!("Unhandled attribute constant: {flag}")), }) .collect::, _>>()?; + for flag in &flags { + scope.import_sys.insert(flag.to_string()); + } + scope.import_macros_c.insert("ZVAL_LONG".to_owned()); let init = format!( "ZVAL_LONG!(&mut (*attrib).args[{index}].value, {flags} as i64);", flags = flags.join("|") @@ -1144,6 +1391,15 @@ impl ArgInfoBuilder<'_> { return Err(format!("Unsupported class attribute: {attrib:?}")); } } + scope.import_sys.insert("zend_string".to_owned()); + scope + .import_sys + .insert("zend_string_init_interned".to_owned()); + scope.import_sys.insert("zend_attribute".to_owned()); + scope + .import_sys + .insert("zend_add_class_attribute".to_owned()); + scope.import_sys.insert("zend_string_release_ex".to_owned()); Ok(format!( "{space}unsafe {{\ \n{space}{space}let name: *mut zend_string = (zend_string_init_interned.expect(\"zend_string_init_interned should be set\"))(\ @@ -1169,6 +1425,7 @@ impl ArgInfoBuilder<'_> { /// Generate code for registering an enum case. fn build_case( &mut self, + scope: &mut ImportsUsed, case: &EnumCase, backing: Option<&BuiltinType>, ) -> Result { @@ -1180,8 +1437,16 @@ impl ArgInfoBuilder<'_> { )); } return match &value.kind { - ExprKind::String(string) => Ok(format!( - "{space}{{\ + ExprKind::String(string) => { + scope.import_sys.insert("zval".to_owned()); + scope.import_sys.insert("zend_string".to_owned()); + scope.import_sys.insert("zend_string_init".to_owned()); + scope + .import_sys + .insert("zend_enum_add_case_cstr".to_owned()); + scope.import_macros_c.insert("ZVAL_STR".to_owned()); + Ok(format!( + "{space}{{\ \n{space3}let mut value: zval = std::mem::zeroed();\ \n{space3}let value_str: *mut zend_string = zend_string_init(\ \n{space3}{space}c\"{val}\".as_ptr(),\ @@ -1191,25 +1456,36 @@ impl ArgInfoBuilder<'_> { \n{space3}ZVAL_STR!(std::ptr::from_mut::(&mut value), value_str);\ \n{space3}zend_enum_add_case_cstr(class_entry, c\"{name}\".as_ptr(), std::ptr::from_mut::(&mut value));\ \n{space}{space}}}", - name = case_name, - val = string, - space = " ", - space3 = " ", - )), - ExprKind::Int(num) => Ok(format!( - "{space}{{\ + name = case_name, + val = string, + space = " ", + space3 = " ", + )) + } + ExprKind::Int(num) => { + scope.import_sys.insert("zval".to_owned()); + scope + .import_sys + .insert("zend_enum_add_case_cstr".to_owned()); + scope.import_macros_c.insert("ZVAL_LONG".to_owned()); + Ok(format!( + "{space}{{\ \n{space3}let mut value: zval = std::mem::zeroed();\ \n{space3}ZVAL_LONG!(std::ptr::from_mut::(&mut value), {val});\ \n{space3}zend_enum_add_case_cstr(class_entry, c\"{name}\".as_ptr(), std::ptr::from_mut::(&mut value));\ \n{space}{space}}}", - name = case_name, - val = num, - space = " ", - space3 = " ", - )), + name = case_name, + val = num, + space = " ", + space3 = " ", + )) + } _ => Err(format!("Unhandled enum backing: {case:#?}")), }; } + scope + .import_sys + .insert("zend_enum_add_case_cstr".to_owned()); Ok(format!( "{space}zend_enum_add_case_cstr(class_entry, c\"{name}\".as_ptr(), std::ptr::null_mut());", name = case_name, @@ -1218,14 +1494,40 @@ impl ArgInfoBuilder<'_> { } /// Combine all of the code parts into the arginfo file contents. - pub(crate) fn assemble(&self) -> String { + pub(crate) fn assemble(&mut self) -> String { + // Some extra imports might be conditional + let mut extra_uses_code = String::new(); let verification_code = if self.needs_verification { + let verification_imports = VERIFICATION_RSPHP_SYS_IMPORTS; + match self.verification { + VerificationMode::Never => (), + VerificationMode::Always => { + self.file_uses.import_sys.extend( + verification_imports + .iter() + .map(|import| (*import).to_owned()), + ); + } + VerificationMode::DebugAssert => { + let extra_verification_imports = &verification_imports + .iter() + .map(|import| (*import).to_owned()) + .collect::>() + - &self.file_uses.import_sys; + if let Some(extra_use) = + ImportsUsed::as_use_single(&extra_verification_imports, "rsphp_sys") + { + extra_uses_code = format!("#[cfg(debug_assertions)]\n{extra_use}"); + } + } + } self.verification.get_infra() } else { String::new() }; format!( - "use rsphp_macros_c::*;\nuse rsphp_macros_rs::*;\nuse rsphp_sys::*;\n{}\n{}\n", + "{}{extra_uses_code}{}\n{}\n", + self.file_uses.as_uses(), verification_code, self.parts.join("\n") ) diff --git a/crates/stub-parser/src/types.rs b/crates/stub-parser/src/types.rs index 6ba95a4..ce91608 100644 --- a/crates/stub-parser/src/types.rs +++ b/crates/stub-parser/src/types.rs @@ -1,6 +1,8 @@ use php_ast::ast::{BuiltinType, NameKind}; use php_ast::owned::{Name, TypeHintKind}; +use crate::builder::ImportsUsed; + /// Information for a typehint for a function or parameter. #[derive(Debug)] #[expect( @@ -12,8 +14,12 @@ pub(crate) struct FunctionTypehint { pub(crate) class_info: Option, /// Info for type masks. pub(crate) mask_info: Option, + /// `rsphp_sys` imports to include when using the type mask. + pub(crate) mask_uses: Vec, /// Info for return types. pub(crate) return_info: Option, + /// `rsphp_sys` imports to use when including the return information. + pub(crate) return_uses: Vec, /// Whether the type is nullable. pub(crate) is_nullable: bool, } @@ -33,6 +39,10 @@ pub(crate) enum ArgInfoTypeRepr { Union(Vec), } +#[expect( + unused_results, + reason = "HashSet used to prevent duplication, just need to include the import" +)] impl ArgInfoTypeRepr { /// Create an `ArgInfoTypeRepr` for the given `TypeHintKind`, resolving /// names in the given namespace. @@ -80,6 +90,7 @@ impl ArgInfoTypeRepr { /// second is the string to actually use. pub(crate) fn property_type_code( &self, + scope: &mut ImportsUsed, namespace: Option<&String>, name_str: &str, ) -> Result<(Option, String), String> { @@ -88,48 +99,80 @@ impl ArgInfoTypeRepr { None => String::new(), }; match self { - ArgInfoTypeRepr::Builtin(_) => Ok(( - None, - format!("ZEND_TYPE_INIT_MASK!({})", self.type_info()?.1), - )), + ArgInfoTypeRepr::Builtin(_) => { + let type_info = self.type_info()?; + scope.import_sys.insert(type_info.1.to_owned()); + scope + .import_macros_c + .insert("ZEND_TYPE_INIT_MASK".to_owned()); + Ok((None, format!("ZEND_TYPE_INIT_MASK!({})", type_info.1))) + } ArgInfoTypeRepr::Nullable(inner) => match **inner { - ArgInfoTypeRepr::Builtin(_) => Ok(( - None, - format!("ZEND_TYPE_INIT_MASK!({}|MAY_BE_NULL)", inner.type_info()?.1), - )), - ArgInfoTypeRepr::Named(_) | ArgInfoTypeRepr::FullyQualifiedNamed(_) => Ok(( - inner.property_type_code(namespace, name_str)?.0, - format!("ZEND_TYPE_INIT_CLASS!(property_{name_str}_type, 0, MAY_BE_NULL)"), - )), + ArgInfoTypeRepr::Builtin(_) => { + let type_info = inner.type_info()?; + scope.import_sys.insert(type_info.1.to_owned()); + scope.import_sys.insert("MAY_BE_NULL".to_owned()); + scope + .import_macros_c + .insert("ZEND_TYPE_INIT_MASK".to_owned()); + Ok(( + None, + format!("ZEND_TYPE_INIT_MASK!({}|MAY_BE_NULL)", type_info.1), + )) + } + ArgInfoTypeRepr::Named(_) | ArgInfoTypeRepr::FullyQualifiedNamed(_) => { + scope.import_sys.insert("MAY_BE_NULL".to_owned()); + scope + .import_macros_c + .insert("ZEND_TYPE_INIT_CLASS".to_owned()); + Ok(( + inner.property_type_code(scope, namespace, name_str)?.0, + format!("ZEND_TYPE_INIT_CLASS!(property_{name_str}_type, 0, MAY_BE_NULL)"), + )) + } _ => Err(format!("Unhandled type code: {self:?}")), }, - ArgInfoTypeRepr::Named(raw_name) => Ok(( - Some(format!( - "{space}let property_{name_str}_type: *mut zend_string = unsafe {{\ - \n{space}{space}zend_string_init(\ - \n{space}{space}{space}c\"{name}\".as_ptr(),\ - \n{space}{space}{space}\"{name}\".len(),\ - \n{space}{space}{space}true,\ - \n{space}{space})\ - \n{space}}};", - name = format!("{namespace_prefix}{raw_name}"), - space = " " - )), - format!("ZEND_TYPE_INIT_CLASS!(property_{name_str}_type, 0, 0)"), - )), - ArgInfoTypeRepr::FullyQualifiedNamed(name) => Ok(( - Some(format!( - "{space}let property_{name_str}_type: *mut zend_string = unsafe {{\ - \n{space}{space}zend_string_init(\ - \n{space}{space}{space}c\"{name}\".as_ptr(),\ - \n{space}{space}{space}\"{name}\".len(),\ - \n{space}{space}{space}true,\ - \n{space}{space})\ - \n{space}}};", - space = " " - )), - format!("ZEND_TYPE_INIT_CLASS!(property_{name_str}_type, 0, 0)"), - )), + ArgInfoTypeRepr::Named(raw_name) => { + scope.import_sys.insert("zend_string".to_owned()); + scope.import_sys.insert("zend_string_init".to_owned()); + scope + .import_macros_c + .insert("ZEND_TYPE_INIT_CLASS".to_owned()); + Ok(( + Some(format!( + "{space}let property_{name_str}_type: *mut zend_string = unsafe {{\ + \n{space}{space}zend_string_init(\ + \n{space}{space}{space}c\"{name}\".as_ptr(),\ + \n{space}{space}{space}\"{name}\".len(),\ + \n{space}{space}{space}true,\ + \n{space}{space})\ + \n{space}}};", + name = format!("{namespace_prefix}{raw_name}"), + space = " " + )), + format!("ZEND_TYPE_INIT_CLASS!(property_{name_str}_type, 0, 0)"), + )) + } + ArgInfoTypeRepr::FullyQualifiedNamed(name) => { + scope.import_sys.insert("zend_string".to_owned()); + scope.import_sys.insert("zend_string_init".to_owned()); + scope + .import_macros_c + .insert("ZEND_TYPE_INIT_CLASS".to_owned()); + Ok(( + Some(format!( + "{space}let property_{name_str}_type: *mut zend_string = unsafe {{\ + \n{space}{space}zend_string_init(\ + \n{space}{space}{space}c\"{name}\".as_ptr(),\ + \n{space}{space}{space}\"{name}\".len(),\ + \n{space}{space}{space}true,\ + \n{space}{space})\ + \n{space}}};", + space = " " + )), + format!("ZEND_TYPE_INIT_CLASS!(property_{name_str}_type, 0, 0)"), + )) + } ArgInfoTypeRepr::Union(inner) => { if inner .iter() @@ -139,6 +182,12 @@ impl ArgInfoTypeRepr { .iter() .map(|inner_type| inner_type.type_info().map(|inner_info| inner_info.1)) .collect::>()?; + for inner_flag in &parts { + scope.import_sys.insert(inner_flag.to_string()); + } + scope + .import_macros_c + .insert("ZEND_TYPE_INIT_MASK".to_owned()); Ok((None, format!("ZEND_TYPE_INIT_MASK!({})", parts.join("|")))) } else { Err(format!("Unhandled union: {inner:?}")) @@ -161,12 +210,19 @@ impl ArgInfoTypeRepr { Some(ns) => format!("{ns}\\\\"), None => String::new(), }; + let mut mask_uses = vec![]; + let mut return_uses = vec![]; let (class_type, mask, for_info) = match without_null { - ArgInfoTypeRepr::Builtin(_) => ( - None, - Some(without_null.type_info()?.1.to_owned()), - Some(without_null.type_info()?.0.to_owned()), - ), + ArgInfoTypeRepr::Builtin(_) => { + let type_info = without_null.type_info()?; + mask_uses.push(type_info.1.to_owned()); + return_uses.push(type_info.0.to_owned()); + ( + None, + Some(type_info.1.to_owned()), + Some(type_info.0.to_owned()), + ) + } ArgInfoTypeRepr::Named(name) => (Some(format!("{namespace_prefix}{name}")), None, None), ArgInfoTypeRepr::FullyQualifiedNamed(name) => (Some(name.clone()), None, None), ArgInfoTypeRepr::Union(inner) => { @@ -178,28 +234,36 @@ impl ArgInfoTypeRepr { if inner_repr.iter().any(|repr| repr.is_nullable) { return Err(format!("Union {inner:#?} contains nullable types")); } - let mask_parts = inner_repr - .iter() - .filter_map(|repr| repr.mask_info.clone()) - .collect::>(); - let info_parts = inner_repr - .iter() - .filter_map(|repr| repr.return_info.clone()) - .collect::>(); - let name_parts = inner_repr - .iter() - .filter_map(|repr| repr.class_info.clone()) - .collect::>(); + let mut total_mask_parts = vec![]; + let mut total_info_parts = vec![]; + let mut total_name_parts = vec![]; + for inner_type in inner_repr { + if let Some(info) = inner_type.mask_info { + total_mask_parts.push(info); + } + if let Some(return_info) = inner_type.return_info { + total_info_parts.push(return_info); + } + if let Some(class_info) = inner_type.class_info { + total_name_parts.push(class_info); + } + mask_uses.extend(inner_type.mask_uses); + return_uses.extend(inner_type.return_uses); + } // Might only contain basic types - if name_parts.is_empty() { - (None, Some(mask_parts.join("|")), Some(info_parts.join("|"))) - } else if mask_parts.is_empty() { - (Some(name_parts.join("|")), None, None) + if total_name_parts.is_empty() { + ( + None, + Some(total_mask_parts.join("|")), + Some(total_info_parts.join("|")), + ) + } else if total_mask_parts.is_empty() { + (Some(total_name_parts.join("|")), None, None) } else { ( - Some(name_parts.join("|")), - Some(mask_parts.join("|")), - Some(info_parts.join("|")), + Some(total_name_parts.join("|")), + Some(total_mask_parts.join("|")), + Some(total_info_parts.join("|")), ) } } @@ -210,13 +274,15 @@ impl ArgInfoTypeRepr { Ok(FunctionTypehint { class_info: class_type, mask_info: mask, + mask_uses, return_info: for_info, + return_uses, is_nullable, }) } /// For builtin types, get the `IS_*` and `MAY_BE_*` flags. - pub(crate) fn type_info(&self) -> Result<(&str, &str), String> { + fn type_info(&self) -> Result<(&str, &str), String> { let ArgInfoTypeRepr::Builtin(php_type) = self else { return Err(format!("type_info() called for {self:#?}")); }; diff --git a/crates/stub-parser/src/verification.rs b/crates/stub-parser/src/verification.rs index 4e03e37..c2963a3 100644 --- a/crates/stub-parser/src/verification.rs +++ b/crates/stub-parser/src/verification.rs @@ -1,4 +1,5 @@ use php_ast::ast::Visibility; +use std::collections::HashSet; use std::fmt::Write as _; /// How should calls to `register_class_*` methods be verified for correctness? @@ -102,6 +103,16 @@ impl VerificationData { } } + /// Get all of the flags that any of the methods use, for adding `use` + /// statements. + pub(crate) fn get_all_flags(methods: &[VerificationData]) -> HashSet { + let mut result = HashSet::new(); + for method in methods { + result.extend(method.flags.clone()); + } + result + } + /// Add a `ZEND_ACC_*` flag based on the visibility to the expected flags. pub(crate) fn add_visibility(&mut self, vis: Option<&Visibility>) { let flag = match vis { @@ -136,6 +147,18 @@ impl VerificationData { } } +/// Imports needed for infrastructure, at a file level. +pub(crate) const VERIFICATION_RSPHP_SYS_IMPORTS: &[&str] = &[ + "_ZEND_TYPE_LIST_BIT", + "_ZEND_TYPE_LITERAL_NAME_BIT", + "_ZEND_TYPE_NAME_BIT", + "zend_function_entry", + "zend_internal_arg_info", + "zend_string", + "zend_type", + "zend_type_list", +]; + /// Infrastructure for registration verification. const VERIFICATION_INFRASTRUCTURE: &[&str] = &[ "\nuse std::ffi::{c_char, CStr};", diff --git a/crates/stub-parser/tests/cases/attribute_registration.expected b/crates/stub-parser/tests/cases/attribute_registration.expected index dd18665..a95a903 100644 --- a/crates/stub-parser/tests/cases/attribute_registration.expected +++ b/crates/stub-parser/tests/cases/attribute_registration.expected @@ -1,12 +1,9 @@ -use rsphp_macros_c::*; -use rsphp_macros_rs::*; -use rsphp_sys::*; /// Module for `CustomCastable` class. pub(crate) mod CustomCastable { - use rsphp_macros_c::*; - use rsphp_macros_rs::*; - use rsphp_sys::*; + use rsphp_macros_c::{INIT_CLASS_ENTRY, ZVAL_LONG}; + use rsphp_macros_rs::ZEND_ARG_INFO_FOR_FUNCTION; + use rsphp_sys::{ZEND_ATTRIBUTE_TARGET_CLASS, zend_add_class_attribute, zend_attribute, zend_class_entry, zend_function_entry, zend_internal_arg_info, zend_register_internal_class_with_flags, zend_string, zend_string_init_interned, zend_string_release_ex}; /// Argument information for `CustomCastable::__construct()`. pub(crate) const ARGINFO___CONSTRUCT: [zend_internal_arg_info; 1] = [ diff --git a/crates/stub-parser/tests/cases/backed_enum.expected b/crates/stub-parser/tests/cases/backed_enum.expected index 14762ab..eddbf0c 100644 --- a/crates/stub-parser/tests/cases/backed_enum.expected +++ b/crates/stub-parser/tests/cases/backed_enum.expected @@ -1,11 +1,8 @@ -use rsphp_macros_c::*; -use rsphp_macros_rs::*; -use rsphp_sys::*; /// Module for `Demo` enum. pub(crate) mod Demo { - use rsphp_macros_c::*; - use rsphp_sys::*; + use rsphp_macros_c::ZVAL_STR; + use rsphp_sys::{IS_STRING, zend_class_entry, zend_enum_add_case_cstr, zend_register_internal_enum, zend_string, zend_string_init, zval}; /// Register the `Demo` enum. diff --git a/crates/stub-parser/tests/cases/class_constant_unknown.expected b/crates/stub-parser/tests/cases/class_constant_unknown.expected index 36ff6cb..4d8260a 100644 --- a/crates/stub-parser/tests/cases/class_constant_unknown.expected +++ b/crates/stub-parser/tests/cases/class_constant_unknown.expected @@ -1,11 +1,8 @@ -use rsphp_macros_c::*; -use rsphp_macros_rs::*; -use rsphp_sys::*; /// Module for `Demo` class. pub(crate) mod Demo { - use rsphp_macros_c::*; - use rsphp_sys::*; + use rsphp_macros_c::{INIT_CLASS_ENTRY, ZEND_TYPE_INIT_MASK, ZVAL_LONG}; + use rsphp_sys::{MAY_BE_LONG, ZEND_ACC_DEPRECATED, ZEND_ACC_PUBLIC, zend_class_entry, zend_declare_typed_class_constant, zend_register_internal_class_with_flags, zend_string, zend_string_init_interned, zend_string_release_ex, zval}; /// Register the `Demo` class. diff --git a/crates/stub-parser/tests/cases/class_constants.expected b/crates/stub-parser/tests/cases/class_constants.expected index bce3c03..ad029ae 100644 --- a/crates/stub-parser/tests/cases/class_constants.expected +++ b/crates/stub-parser/tests/cases/class_constants.expected @@ -1,12 +1,9 @@ -use rsphp_macros_c::*; -use rsphp_macros_rs::*; -use rsphp_sys::*; /// Module for `CustomCastable` class. pub(crate) mod CustomCastable { - use rsphp_macros_c::*; - use rsphp_macros_rs::*; - use rsphp_sys::*; + use rsphp_macros_c::{INIT_CLASS_ENTRY, ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE, ZEND_TYPE_INIT_MASK, ZVAL_LONG, ZVAL_UNDEF}; + use rsphp_macros_rs::ZEND_ARG_INFO_FOR_FUNCTION; + use rsphp_sys::{IS_LONG, MAY_BE_LONG, ZEND_ACC_FINAL, ZEND_ACC_PRIVATE, ZEND_ACC_PUBLIC, zend_class_entry, zend_declare_typed_class_constant, zend_declare_typed_property, zend_function_entry, zend_internal_arg_info, zend_register_internal_class_with_flags, zend_string, zend_string_init, zend_string_init_interned, zend_string_release, zend_string_release_ex, zval}; /// Argument information for `CustomCastable::__construct()`. pub(crate) const ARGINFO___CONSTRUCT: [zend_internal_arg_info; 2] = [ @@ -111,8 +108,8 @@ pub(crate) mod CustomCastable { } /// Module for `ConstantValues` class. pub(crate) mod ConstantValues { - use rsphp_macros_c::*; - use rsphp_sys::*; + use rsphp_macros_c::{INIT_CLASS_ENTRY, ZEND_TYPE_INIT_MASK, ZVAL_DOUBLE, ZVAL_STR}; + use rsphp_sys::{MAY_BE_DOUBLE, MAY_BE_STRING, PHP_OS, ZEND_ACC_PUBLIC, ZEND_DEBUG, zend_class_entry, zend_declare_typed_class_constant, zend_register_internal_class_with_flags, zend_string, zend_string_init, zend_string_init_interned, zend_string_release_ex, zval}; /// Register the `ConstantValues` class. @@ -144,8 +141,8 @@ pub(crate) mod ConstantValues { unsafe { let mut value: zval = std::mem::zeroed(); let value_str: *mut zend_string = zend_string_init( - crate::PHP_OS.as_ptr().cast::(), - std::ffi::CStr::from_ptr(crate::PHP_OS.as_ptr().cast::()).count_bytes(), + PHP_OS.as_ptr().cast::(), + std::ffi::CStr::from_ptr(PHP_OS.as_ptr().cast::()).count_bytes(), true); ZVAL_STR!(&mut value, value_str); let name: *mut zend_string = (zend_string_init_interned.expect("zend_string_init_interned should be set"))( @@ -164,8 +161,8 @@ pub(crate) mod ConstantValues { } /// Module for `ImplicitPublicConstant` class. pub(crate) mod ImplicitPublicConstant { - use rsphp_macros_c::*; - use rsphp_sys::*; + use rsphp_macros_c::{INIT_CLASS_ENTRY, ZEND_TYPE_INIT_MASK, ZVAL_LONG}; + use rsphp_sys::{MAY_BE_LONG, ZEND_ACC_PUBLIC, zend_class_entry, zend_declare_typed_class_constant, zend_register_internal_class_with_flags, zend_string, zend_string_init_interned, zend_string_release_ex, zval}; /// Register the `ImplicitPublicConstant` class. diff --git a/crates/stub-parser/tests/cases/class_extends.expected b/crates/stub-parser/tests/cases/class_extends.expected index 6ecb1af..9ce7715 100644 --- a/crates/stub-parser/tests/cases/class_extends.expected +++ b/crates/stub-parser/tests/cases/class_extends.expected @@ -1,11 +1,8 @@ -use rsphp_macros_c::*; -use rsphp_macros_rs::*; -use rsphp_sys::*; /// Module for `Base` class. pub(crate) mod Base { - use rsphp_macros_c::*; - use rsphp_sys::*; + use rsphp_macros_c::INIT_CLASS_ENTRY; + use rsphp_sys::{zend_class_entry, zend_register_internal_class_with_flags}; /// Register the `Base` class. @@ -26,8 +23,8 @@ pub(crate) mod Base { } /// Module for `Demo` class. pub(crate) mod Demo { - use rsphp_macros_c::*; - use rsphp_sys::*; + use rsphp_macros_c::INIT_CLASS_ENTRY; + use rsphp_sys::{zend_class_entry, zend_register_internal_class_with_flags}; /// Register the `Demo` class. diff --git a/crates/stub-parser/tests/cases/class_flags.expected b/crates/stub-parser/tests/cases/class_flags.expected index 263c7e6..30166aa 100644 --- a/crates/stub-parser/tests/cases/class_flags.expected +++ b/crates/stub-parser/tests/cases/class_flags.expected @@ -1,11 +1,8 @@ -use rsphp_macros_c::*; -use rsphp_macros_rs::*; -use rsphp_sys::*; /// Module for `Demo` class. pub(crate) mod Demo { - use rsphp_macros_c::*; - use rsphp_sys::*; + use rsphp_macros_c::INIT_CLASS_ENTRY; + use rsphp_sys::{ZEND_ACC_FINAL, ZEND_ACC_NO_DYNAMIC_PROPERTIES, zend_class_entry, zend_register_internal_class_with_flags}; /// Register the `Demo` class. @@ -26,8 +23,8 @@ pub(crate) mod Demo { } /// Module for `Demo2` class. pub(crate) mod Demo2 { - use rsphp_macros_c::*; - use rsphp_sys::*; + use rsphp_macros_c::INIT_CLASS_ENTRY; + use rsphp_sys::{ZEND_ACC_FINAL, ZEND_ACC_NOT_SERIALIZABLE, zend_class_entry, zend_register_internal_class_with_flags}; /// Register the `Demo2` class. @@ -48,8 +45,8 @@ pub(crate) mod Demo2 { } /// Module for `Demo3` class. pub(crate) mod Demo3 { - use rsphp_macros_c::*; - use rsphp_sys::*; + use rsphp_macros_c::INIT_CLASS_ENTRY; + use rsphp_sys::{ZEND_ACC_FINAL, ZEND_ACC_NOT_SERIALIZABLE, ZEND_ACC_NO_DYNAMIC_PROPERTIES, zend_class_entry, zend_register_internal_class_with_flags}; /// Register the `Demo3` class. diff --git a/crates/stub-parser/tests/cases/class_implements.expected b/crates/stub-parser/tests/cases/class_implements.expected index b33a551..2263ebc 100644 --- a/crates/stub-parser/tests/cases/class_implements.expected +++ b/crates/stub-parser/tests/cases/class_implements.expected @@ -1,11 +1,8 @@ -use rsphp_macros_c::*; -use rsphp_macros_rs::*; -use rsphp_sys::*; /// Module for `IDemo` interface. pub(crate) mod IDemo { - use rsphp_macros_c::*; - use rsphp_sys::*; + use rsphp_macros_c::INIT_CLASS_ENTRY; + use rsphp_sys::{zend_class_entry, zend_register_internal_interface}; /// Register the `IDemo` interface. @@ -24,8 +21,8 @@ pub(crate) mod IDemo { } /// Module for `Demo` class. pub(crate) mod Demo { - use rsphp_macros_c::*; - use rsphp_sys::*; + use rsphp_macros_c::INIT_CLASS_ENTRY; + use rsphp_sys::{zend_class_entry, zend_class_implements, zend_register_internal_class_with_flags}; /// Register the `Demo` class. diff --git a/crates/stub-parser/tests/cases/class_registration.expected b/crates/stub-parser/tests/cases/class_registration.expected index 42faa10..09709de 100644 --- a/crates/stub-parser/tests/cases/class_registration.expected +++ b/crates/stub-parser/tests/cases/class_registration.expected @@ -1,11 +1,8 @@ -use rsphp_macros_c::*; -use rsphp_macros_rs::*; -use rsphp_sys::*; /// Module for `Demo` class. pub(crate) mod Demo { - use rsphp_macros_c::*; - use rsphp_sys::*; + use rsphp_macros_c::{INIT_CLASS_ENTRY, ZEND_TYPE_INIT_MASK, ZVAL_UNDEF}; + use rsphp_sys::{MAY_BE_LONG, ZEND_ACC_PRIVATE, zend_class_entry, zend_declare_typed_property, zend_register_internal_class_with_flags, zend_string, zend_string_init, zend_string_release, zval}; /// Register the `Demo` class. diff --git a/crates/stub-parser/tests/cases/default_unknown_param.expected b/crates/stub-parser/tests/cases/default_unknown_param.expected index 71296cc..22636cd 100644 --- a/crates/stub-parser/tests/cases/default_unknown_param.expected +++ b/crates/stub-parser/tests/cases/default_unknown_param.expected @@ -1,12 +1,9 @@ -use rsphp_macros_c::*; -use rsphp_macros_rs::*; -use rsphp_sys::*; /// Module for `Demo` class. pub(crate) mod Demo { - use rsphp_macros_c::*; - use rsphp_macros_rs::*; - use rsphp_sys::*; + use rsphp_macros_c::{INIT_CLASS_ENTRY, ZEND_ARG_TYPE_INFO}; + use rsphp_macros_rs::ZEND_ARG_INFO_FOR_FUNCTION_WITH_RETURN_TYPE_MASK; + use rsphp_sys::{IS_MIXED, MAY_BE_VOID, zend_class_entry, zend_function_entry, zend_internal_arg_info, zend_register_internal_class_with_flags}; /// Argument information for `Demo::test1()`. pub(crate) const ARGINFO_TEST1: [zend_internal_arg_info; 2] = [ diff --git a/crates/stub-parser/tests/cases/enum.expected b/crates/stub-parser/tests/cases/enum.expected index 7875851..8936ff0 100644 --- a/crates/stub-parser/tests/cases/enum.expected +++ b/crates/stub-parser/tests/cases/enum.expected @@ -1,11 +1,7 @@ -use rsphp_macros_c::*; -use rsphp_macros_rs::*; -use rsphp_sys::*; /// Module for `CustomCasting\CastableTarget` enum. pub(crate) mod CustomCasting_CastableTarget { - use rsphp_macros_c::*; - use rsphp_sys::*; + use rsphp_sys::{IS_UNDEF, zend_class_entry, zend_enum_add_case_cstr, zend_register_internal_enum}; /// Register the `CustomCasting\CastableTarget` enum. diff --git a/crates/stub-parser/tests/cases/enum_types.expected b/crates/stub-parser/tests/cases/enum_types.expected index d652b41..c9dd84a 100644 --- a/crates/stub-parser/tests/cases/enum_types.expected +++ b/crates/stub-parser/tests/cases/enum_types.expected @@ -1,11 +1,7 @@ -use rsphp_macros_c::*; -use rsphp_macros_rs::*; -use rsphp_sys::*; /// Module for `Unbacked` enum. pub(crate) mod Unbacked { - use rsphp_macros_c::*; - use rsphp_sys::*; + use rsphp_sys::{IS_UNDEF, zend_class_entry, zend_enum_add_case_cstr, zend_register_internal_enum}; /// Register the `Unbacked` enum. @@ -28,8 +24,8 @@ pub(crate) mod Unbacked { } /// Module for `IntBacked` enum. pub(crate) mod IntBacked { - use rsphp_macros_c::*; - use rsphp_sys::*; + use rsphp_macros_c::ZVAL_LONG; + use rsphp_sys::{IS_LONG, zend_class_entry, zend_enum_add_case_cstr, zend_register_internal_enum, zval}; /// Register the `IntBacked` enum. @@ -60,8 +56,8 @@ pub(crate) mod IntBacked { } /// Module for `StringBacked` enum. pub(crate) mod StringBacked { - use rsphp_macros_c::*; - use rsphp_sys::*; + use rsphp_macros_c::ZVAL_STR; + use rsphp_sys::{IS_STRING, zend_class_entry, zend_enum_add_case_cstr, zend_register_internal_enum, zend_string, zend_string_init, zval}; /// Register the `StringBacked` enum. diff --git a/crates/stub-parser/tests/cases/functions.expected b/crates/stub-parser/tests/cases/functions.expected index 3f0f265..ea660eb 100644 --- a/crates/stub-parser/tests/cases/functions.expected +++ b/crates/stub-parser/tests/cases/functions.expected @@ -1,6 +1,6 @@ -use rsphp_macros_c::*; -use rsphp_macros_rs::*; -use rsphp_sys::*; +use rsphp_macros_c::{ZEND_ARG_INFO, ZEND_ARG_INFO_WITH_DEFAULT_VALUE, ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE}; +use rsphp_macros_rs::{ZEND_ARG_INFO_FOR_FUNCTION, ZEND_ARG_INFO_FOR_FUNCTION_WITH_RETURN_TYPE_MASK}; +use rsphp_sys::{IS_LONG, IS_STRING, MAY_BE_LONG, MAY_BE_STRING, MAY_BE_VOID, zend_internal_arg_info}; /// Argument information for `test1()`. pub(crate) const ARGINFO_TEST1: [zend_internal_arg_info; 1] = [ diff --git a/crates/stub-parser/tests/cases/interface_extends.expected b/crates/stub-parser/tests/cases/interface_extends.expected index 6558e09..d2a2061 100644 --- a/crates/stub-parser/tests/cases/interface_extends.expected +++ b/crates/stub-parser/tests/cases/interface_extends.expected @@ -1,11 +1,8 @@ -use rsphp_macros_c::*; -use rsphp_macros_rs::*; -use rsphp_sys::*; /// Module for `HasMagic` interface. pub(crate) mod HasMagic { - use rsphp_macros_c::*; - use rsphp_sys::*; + use rsphp_macros_c::INIT_CLASS_ENTRY; + use rsphp_sys::{zend_class_entry, zend_class_implements, zend_register_internal_interface}; /// Register the `HasMagic` interface. diff --git a/crates/stub-parser/tests/cases/interface_registration.expected b/crates/stub-parser/tests/cases/interface_registration.expected index ce5da48..f812ec5 100644 --- a/crates/stub-parser/tests/cases/interface_registration.expected +++ b/crates/stub-parser/tests/cases/interface_registration.expected @@ -1,11 +1,7 @@ -use rsphp_macros_c::*; -use rsphp_macros_rs::*; -use rsphp_sys::*; /// Module for `CustomCasting\CastableTarget` enum. pub(crate) mod CustomCasting_CastableTarget { - use rsphp_macros_c::*; - use rsphp_sys::*; + use rsphp_sys::{IS_UNDEF, zend_class_entry, zend_enum_add_case_cstr, zend_register_internal_enum}; /// Register the `CustomCasting\CastableTarget` enum. @@ -29,9 +25,9 @@ pub(crate) mod CustomCasting_CastableTarget { } /// Module for `CustomCasting\HasCustomCast` interface. pub(crate) mod CustomCasting_HasCustomCast { - use rsphp_macros_c::*; - use rsphp_macros_rs::*; - use rsphp_sys::*; + use rsphp_macros_c::{INIT_NS_CLASS_ENTRY, ZEND_ARG_OBJ_INFO}; + use rsphp_macros_rs::ZEND_ARG_INFO_FOR_FUNCTION; + use rsphp_sys::{zend_class_entry, zend_function_entry, zend_internal_arg_info, zend_register_internal_interface}; /// Argument information for `CustomCasting\HasCustomCast::__doCast()`. pub(crate) const ARGINFO___DOCAST: [zend_internal_arg_info; 2] = [ diff --git a/crates/stub-parser/tests/cases/method_attribs.expected b/crates/stub-parser/tests/cases/method_attribs.expected index 8fe6dfb..e423ced 100644 --- a/crates/stub-parser/tests/cases/method_attribs.expected +++ b/crates/stub-parser/tests/cases/method_attribs.expected @@ -1,12 +1,9 @@ -use rsphp_macros_c::*; -use rsphp_macros_rs::*; -use rsphp_sys::*; /// Module for `Demo` class. pub(crate) mod Demo { - use rsphp_macros_c::*; - use rsphp_macros_rs::*; - use rsphp_sys::*; + use rsphp_macros_c::{INIT_CLASS_ENTRY, ZSTR_KNOWN, ZSTR_LEN, ZVAL_STR}; + use rsphp_macros_rs::ZEND_ARG_INFO_FOR_FUNCTION_WITH_RETURN_TYPE_MASK; + use rsphp_sys::{HashTable, MAY_BE_VOID, _zend_known_string_id, zend_add_function_attribute, zend_attribute, zend_class_entry, zend_function, zend_function_entry, zend_hash_str_find_ptr, zend_internal_arg_info, zend_register_internal_class_with_flags, zend_string_init, zval}; /// Argument information for `Demo::test1()`. pub(crate) const ARGINFO_TEST1: [zend_internal_arg_info; 1] = [ diff --git a/crates/stub-parser/tests/cases/methods.expected b/crates/stub-parser/tests/cases/methods.expected index e98a96c..ce5b6e2 100644 --- a/crates/stub-parser/tests/cases/methods.expected +++ b/crates/stub-parser/tests/cases/methods.expected @@ -1,12 +1,9 @@ -use rsphp_macros_c::*; -use rsphp_macros_rs::*; -use rsphp_sys::*; /// Module for `Demo` class. pub(crate) mod Demo { - use rsphp_macros_c::*; - use rsphp_macros_rs::*; - use rsphp_sys::*; + use rsphp_macros_c::{INIT_CLASS_ENTRY, ZEND_ARG_INFO, ZEND_ARG_INFO_WITH_DEFAULT_VALUE, ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE}; + use rsphp_macros_rs::{ZEND_ARG_INFO_FOR_FUNCTION, ZEND_ARG_INFO_FOR_FUNCTION_WITH_RETURN_TYPE_MASK}; + use rsphp_sys::{IS_LONG, IS_STRING, MAY_BE_LONG, MAY_BE_STRING, MAY_BE_VOID, zend_class_entry, zend_function_entry, zend_internal_arg_info, zend_register_internal_class_with_flags}; /// Argument information for `Demo::test1()`. pub(crate) const ARGINFO_TEST1: [zend_internal_arg_info; 1] = [ diff --git a/crates/stub-parser/tests/cases/namespaced_bodies.expected b/crates/stub-parser/tests/cases/namespaced_bodies.expected index c28f209..209e993 100644 --- a/crates/stub-parser/tests/cases/namespaced_bodies.expected +++ b/crates/stub-parser/tests/cases/namespaced_bodies.expected @@ -1,12 +1,11 @@ -use rsphp_macros_c::*; -use rsphp_macros_rs::*; -use rsphp_sys::*; +use rsphp_macros_rs::ZEND_ARG_INFO_FOR_FUNCTION_WITH_RETURN_TYPE_MASK; +use rsphp_sys::{MAY_BE_ANY, MAY_BE_VOID, zend_internal_arg_info}; /// Module for `Demo\InDemoNamespace` class. pub(crate) mod Demo_InDemoNamespace { - use rsphp_macros_c::*; - use rsphp_macros_rs::*; - use rsphp_sys::*; + use rsphp_macros_c::INIT_NS_CLASS_ENTRY; + use rsphp_macros_rs::ZEND_ARG_INFO_FOR_FUNCTION; + use rsphp_sys::{zend_class_entry, zend_function_entry, zend_internal_arg_info, zend_register_internal_class_with_flags}; /// Argument information for `Demo\InDemoNamespace::__construct()`. pub(crate) const ARGINFO___CONSTRUCT: [zend_internal_arg_info; 1] = [ @@ -38,9 +37,9 @@ pub(crate) const ARGINFO_DEMO_INDEMONAMESPACE: [zend_internal_arg_info; 1] = [ /// Module for `Other\InOtherNamespace` class. pub(crate) mod Other_InOtherNamespace { - use rsphp_macros_c::*; - use rsphp_macros_rs::*; - use rsphp_sys::*; + use rsphp_macros_c::INIT_NS_CLASS_ENTRY; + use rsphp_macros_rs::ZEND_ARG_INFO_FOR_FUNCTION; + use rsphp_sys::{zend_class_entry, zend_function_entry, zend_internal_arg_info, zend_register_internal_class_with_flags}; /// Argument information for `Other\InOtherNamespace::__construct()`. pub(crate) const ARGINFO___CONSTRUCT: [zend_internal_arg_info; 1] = [ diff --git a/crates/stub-parser/tests/cases/namespaced_default.expected b/crates/stub-parser/tests/cases/namespaced_default.expected index f42c438..486ada1 100644 --- a/crates/stub-parser/tests/cases/namespaced_default.expected +++ b/crates/stub-parser/tests/cases/namespaced_default.expected @@ -1,12 +1,9 @@ -use rsphp_macros_c::*; -use rsphp_macros_rs::*; -use rsphp_sys::*; /// Module for `CustomCasting\CustomCastable` class. pub(crate) mod CustomCasting_CustomCastable { - use rsphp_macros_c::*; - use rsphp_macros_rs::*; - use rsphp_sys::*; + use rsphp_macros_c::{INIT_NS_CLASS_ENTRY, ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE, ZEND_TYPE_INIT_MASK, ZVAL_LONG}; + use rsphp_macros_rs::ZEND_ARG_INFO_FOR_FUNCTION; + use rsphp_sys::{IS_LONG, MAY_BE_LONG, ZEND_ACC_FINAL, ZEND_ACC_PUBLIC, zend_class_entry, zend_declare_typed_class_constant, zend_function_entry, zend_internal_arg_info, zend_register_internal_class_with_flags, zend_string, zend_string_init_interned, zend_string_release_ex, zval}; /// Argument information for `CustomCasting\CustomCastable::__construct()`. pub(crate) const ARGINFO___CONSTRUCT: [zend_internal_arg_info; 2] = [ diff --git a/crates/stub-parser/tests/cases/namespaced_semi.expected b/crates/stub-parser/tests/cases/namespaced_semi.expected index 86c70e7..877a15c 100644 --- a/crates/stub-parser/tests/cases/namespaced_semi.expected +++ b/crates/stub-parser/tests/cases/namespaced_semi.expected @@ -1,12 +1,11 @@ -use rsphp_macros_c::*; -use rsphp_macros_rs::*; -use rsphp_sys::*; +use rsphp_macros_rs::ZEND_ARG_INFO_FOR_FUNCTION_WITH_RETURN_TYPE_MASK; +use rsphp_sys::{MAY_BE_VOID, zend_internal_arg_info}; /// Module for `Demo\InDemoNamespace` class. pub(crate) mod Demo_InDemoNamespace { - use rsphp_macros_c::*; - use rsphp_macros_rs::*; - use rsphp_sys::*; + use rsphp_macros_c::INIT_NS_CLASS_ENTRY; + use rsphp_macros_rs::ZEND_ARG_INFO_FOR_FUNCTION; + use rsphp_sys::{zend_class_entry, zend_function_entry, zend_internal_arg_info, zend_register_internal_class_with_flags}; /// Argument information for `Demo\InDemoNamespace::__construct()`. pub(crate) const ARGINFO___CONSTRUCT: [zend_internal_arg_info; 1] = [ diff --git a/crates/stub-parser/tests/cases/namespaced_types.expected b/crates/stub-parser/tests/cases/namespaced_types.expected index ce5da48..f812ec5 100644 --- a/crates/stub-parser/tests/cases/namespaced_types.expected +++ b/crates/stub-parser/tests/cases/namespaced_types.expected @@ -1,11 +1,7 @@ -use rsphp_macros_c::*; -use rsphp_macros_rs::*; -use rsphp_sys::*; /// Module for `CustomCasting\CastableTarget` enum. pub(crate) mod CustomCasting_CastableTarget { - use rsphp_macros_c::*; - use rsphp_sys::*; + use rsphp_sys::{IS_UNDEF, zend_class_entry, zend_enum_add_case_cstr, zend_register_internal_enum}; /// Register the `CustomCasting\CastableTarget` enum. @@ -29,9 +25,9 @@ pub(crate) mod CustomCasting_CastableTarget { } /// Module for `CustomCasting\HasCustomCast` interface. pub(crate) mod CustomCasting_HasCustomCast { - use rsphp_macros_c::*; - use rsphp_macros_rs::*; - use rsphp_sys::*; + use rsphp_macros_c::{INIT_NS_CLASS_ENTRY, ZEND_ARG_OBJ_INFO}; + use rsphp_macros_rs::ZEND_ARG_INFO_FOR_FUNCTION; + use rsphp_sys::{zend_class_entry, zend_function_entry, zend_internal_arg_info, zend_register_internal_interface}; /// Argument information for `CustomCasting\HasCustomCast::__doCast()`. pub(crate) const ARGINFO___DOCAST: [zend_internal_arg_info; 2] = [ diff --git a/crates/stub-parser/tests/cases/nullable_property.expected b/crates/stub-parser/tests/cases/nullable_property.expected index 2d36f6c..41e8f24 100644 --- a/crates/stub-parser/tests/cases/nullable_property.expected +++ b/crates/stub-parser/tests/cases/nullable_property.expected @@ -1,11 +1,8 @@ -use rsphp_macros_c::*; -use rsphp_macros_rs::*; -use rsphp_sys::*; /// Module for `Reference` class. pub(crate) mod Reference { - use rsphp_macros_c::*; - use rsphp_sys::*; + use rsphp_macros_c::{INIT_CLASS_ENTRY, ZEND_TYPE_INIT_MASK, ZVAL_UNDEF}; + use rsphp_sys::{MAY_BE_NULL, MAY_BE_STRING, ZEND_ACC_FINAL, ZEND_ACC_NOT_SERIALIZABLE, ZEND_ACC_NO_DYNAMIC_PROPERTIES, ZEND_ACC_PUBLIC, zend_class_entry, zend_declare_typed_property, zend_register_internal_class_with_flags, zend_string, zend_string_init, zend_string_release, zval}; /// Register the `Reference` class. diff --git a/crates/stub-parser/tests/cases/optional_object.expected b/crates/stub-parser/tests/cases/optional_object.expected index f6d7a8e..d70b142 100644 --- a/crates/stub-parser/tests/cases/optional_object.expected +++ b/crates/stub-parser/tests/cases/optional_object.expected @@ -1,12 +1,9 @@ -use rsphp_macros_c::*; -use rsphp_macros_rs::*; -use rsphp_sys::*; /// Module for `Repository` class. pub(crate) mod Repository { - use rsphp_macros_c::*; - use rsphp_macros_rs::*; - use rsphp_sys::*; + use rsphp_macros_c::{INIT_CLASS_ENTRY, ZEND_ARG_OBJ_INFO, ZEND_ARG_OBJ_INFO_WITH_DEFAULT_VALUE, ZEND_ARG_TYPE_INFO}; + use rsphp_macros_rs::ZEND_ARG_INFO_FOR_FUNCTION_WITH_RETURN_OBJ_INFO; + use rsphp_sys::{IS_ARRAY, IS_STRING, ZEND_ACC_FINAL, ZEND_ACC_NOT_SERIALIZABLE, ZEND_ACC_NO_DYNAMIC_PROPERTIES, zend_class_entry, zend_function_entry, zend_internal_arg_info, zend_register_internal_class_with_flags}; /// Argument information for `Repository::commit()`. pub(crate) const ARGINFO_COMMIT: [zend_internal_arg_info; 6] = [ @@ -38,8 +35,8 @@ pub(crate) mod Repository { } /// Module for `Oid` class. pub(crate) mod Oid { - use rsphp_macros_c::*; - use rsphp_sys::*; + use rsphp_macros_c::INIT_CLASS_ENTRY; + use rsphp_sys::{ZEND_ACC_FINAL, ZEND_ACC_NOT_SERIALIZABLE, ZEND_ACC_NO_DYNAMIC_PROPERTIES, zend_class_entry, zend_register_internal_class_with_flags}; /// Register the `Oid` class. @@ -60,8 +57,8 @@ pub(crate) mod Oid { } /// Module for `Signature` class. pub(crate) mod Signature { - use rsphp_macros_c::*; - use rsphp_sys::*; + use rsphp_macros_c::INIT_CLASS_ENTRY; + use rsphp_sys::{ZEND_ACC_FINAL, ZEND_ACC_NOT_SERIALIZABLE, ZEND_ACC_NO_DYNAMIC_PROPERTIES, zend_class_entry, zend_register_internal_class_with_flags}; /// Register the `Signature` class. diff --git a/crates/stub-parser/tests/cases/parameter_types.expected b/crates/stub-parser/tests/cases/parameter_types.expected index adabb0a..dd00c83 100644 --- a/crates/stub-parser/tests/cases/parameter_types.expected +++ b/crates/stub-parser/tests/cases/parameter_types.expected @@ -1,6 +1,6 @@ -use rsphp_macros_c::*; -use rsphp_macros_rs::*; -use rsphp_sys::*; +use rsphp_macros_c::{ZEND_ARG_INFO, ZEND_ARG_INFO_WITH_DEFAULT_VALUE, ZEND_ARG_OBJ_INFO, ZEND_ARG_OBJ_TYPE_MASK, ZEND_ARG_TYPE_MASK}; +use rsphp_macros_rs::ZEND_ARG_INFO_FOR_FUNCTION_WITH_RETURN_TYPE_MASK; +use rsphp_sys::{MAY_BE_BOOL, MAY_BE_LONG, MAY_BE_STRING, MAY_BE_VOID, zend_internal_arg_info}; /// Argument information for `noType()`. pub(crate) const ARGINFO_NOTYPE: [zend_internal_arg_info; 2] = [ diff --git a/crates/stub-parser/tests/cases/property_defaults.expected b/crates/stub-parser/tests/cases/property_defaults.expected index 3b45d23..1bd6184 100644 --- a/crates/stub-parser/tests/cases/property_defaults.expected +++ b/crates/stub-parser/tests/cases/property_defaults.expected @@ -1,11 +1,8 @@ -use rsphp_macros_c::*; -use rsphp_macros_rs::*; -use rsphp_sys::*; /// Module for `Demo` class. pub(crate) mod Demo { - use rsphp_macros_c::*; - use rsphp_sys::*; + use rsphp_macros_c::{INIT_CLASS_ENTRY, ZEND_TYPE_INIT_MASK, ZVAL_EMPTY_ARRAY, ZVAL_EMPTY_STRING, ZVAL_FALSE, ZVAL_INTERNED_STR, ZVAL_LONG, ZVAL_NULL, ZVAL_TRUE}; + use rsphp_sys::{MAY_BE_ANY, ZEND_ACC_PUBLIC, zend_class_entry, zend_declare_typed_property, zend_register_internal_class_with_flags, zend_string, zend_string_init, zend_string_release, zval}; /// Register the `Demo` class. @@ -114,6 +111,7 @@ pub(crate) mod Demo { let mut property_str_other_default_value: zval = unsafe { std::mem::zeroed() }; let property_str_other_default_str = unsafe { zend_string_init(c"foo".as_ptr(), "foo".len(), true) }; + unsafe { ZVAL_INTERNED_STR!(std::ptr::from_mut::(&mut property_str_other_default_value), property_str_other_default_str); } let property_str_other_name: *mut zend_string = unsafe { zend_string_init( diff --git a/crates/stub-parser/tests/cases/tentative_return.expected b/crates/stub-parser/tests/cases/tentative_return.expected index 0984cdd..c7104b3 100644 --- a/crates/stub-parser/tests/cases/tentative_return.expected +++ b/crates/stub-parser/tests/cases/tentative_return.expected @@ -1,12 +1,9 @@ -use rsphp_macros_c::*; -use rsphp_macros_rs::*; -use rsphp_sys::*; /// Module for `Demo` class. pub(crate) mod Demo { - use rsphp_macros_c::*; - use rsphp_macros_rs::*; - use rsphp_sys::*; + use rsphp_macros_c::INIT_CLASS_ENTRY; + use rsphp_macros_rs::{ZEND_ARG_INFO_FOR_FUNCTION_WITH_RETURN_OBJ_INFO, ZEND_ARG_INFO_FOR_FUNCTION_WITH_RETURN_TYPE_MASK}; + use rsphp_sys::{MAY_BE_ARRAY, MAY_BE_LONG, MAY_BE_STRING, zend_class_entry, zend_function_entry, zend_internal_arg_info, zend_register_internal_class_with_flags}; /// Argument information for `Demo::test1()`. pub(crate) const ARGINFO_TEST1: [zend_internal_arg_info; 1] = [ diff --git a/crates/stub-parser/tests/cases/typed_properties.expected b/crates/stub-parser/tests/cases/typed_properties.expected index 901fcf1..075309a 100644 --- a/crates/stub-parser/tests/cases/typed_properties.expected +++ b/crates/stub-parser/tests/cases/typed_properties.expected @@ -1,11 +1,8 @@ -use rsphp_macros_c::*; -use rsphp_macros_rs::*; -use rsphp_sys::*; /// Module for `RSGit\Commit` class. pub(crate) mod RSGit_Commit { - use rsphp_macros_c::*; - use rsphp_sys::*; + use rsphp_macros_c::{INIT_NS_CLASS_ENTRY, ZEND_TYPE_INIT_CLASS, ZVAL_UNDEF}; + use rsphp_sys::{ZEND_ACC_FINAL, ZEND_ACC_NOT_SERIALIZABLE, ZEND_ACC_NO_DYNAMIC_PROPERTIES, ZEND_ACC_PUBLIC, zend_class_entry, zend_declare_typed_property, zend_register_internal_class_with_flags, zend_string, zend_string_init, zend_string_release, zval}; /// Register the `RSGit\Commit` class. @@ -115,9 +112,9 @@ pub(crate) mod RSGit_Commit { } /// Module for `RSGit\Oid` class. pub(crate) mod RSGit_Oid { - use rsphp_macros_c::*; - use rsphp_macros_rs::*; - use rsphp_sys::*; + use rsphp_macros_c::{INIT_NS_CLASS_ENTRY, ZEND_TYPE_INIT_MASK, ZVAL_UNDEF}; + use rsphp_macros_rs::ZEND_ARG_INFO_FOR_FUNCTION_WITH_RETURN_TYPE_MASK; + use rsphp_sys::{MAY_BE_STRING, ZEND_ACC_FINAL, ZEND_ACC_NOT_SERIALIZABLE, ZEND_ACC_NO_DYNAMIC_PROPERTIES, ZEND_ACC_PUBLIC, zend_class_entry, zend_declare_typed_property, zend_function_entry, zend_internal_arg_info, zend_register_internal_class_with_flags, zend_string, zend_string_init, zend_string_release, zval}; /// Argument information for `RSGit\Oid::__toString()`. pub(crate) const ARGINFO___TOSTRING: [zend_internal_arg_info; 1] = [ @@ -166,8 +163,8 @@ pub(crate) mod RSGit_Oid { } /// Module for `RSGit\Signature` class. pub(crate) mod RSGit_Signature { - use rsphp_macros_c::*; - use rsphp_sys::*; + use rsphp_macros_c::{INIT_NS_CLASS_ENTRY, ZEND_TYPE_INIT_CLASS, ZEND_TYPE_INIT_MASK, ZVAL_UNDEF}; + use rsphp_sys::{MAY_BE_STRING, ZEND_ACC_FINAL, ZEND_ACC_NOT_SERIALIZABLE, ZEND_ACC_NO_DYNAMIC_PROPERTIES, ZEND_ACC_PUBLIC, zend_class_entry, zend_declare_typed_property, zend_register_internal_class_with_flags, zend_string, zend_string_init, zend_string_release, zval}; /// Register the `RSGit\Signature` class. diff --git a/crates/stub-parser/tests/cases/variadic_param.expected b/crates/stub-parser/tests/cases/variadic_param.expected index 1e53512..5c9def7 100644 --- a/crates/stub-parser/tests/cases/variadic_param.expected +++ b/crates/stub-parser/tests/cases/variadic_param.expected @@ -1,12 +1,9 @@ -use rsphp_macros_c::*; -use rsphp_macros_rs::*; -use rsphp_sys::*; /// Module for `Demo` class. pub(crate) mod Demo { - use rsphp_macros_c::*; - use rsphp_macros_rs::*; - use rsphp_sys::*; + use rsphp_macros_c::{INIT_CLASS_ENTRY, ZEND_ARG_VARIADIC_TYPE_INFO}; + use rsphp_macros_rs::ZEND_ARG_INFO_FOR_FUNCTION_WITH_RETURN_TYPE_MASK; + use rsphp_sys::{IS_MIXED, MAY_BE_VOID, zend_class_entry, zend_function_entry, zend_internal_arg_info, zend_register_internal_class_with_flags}; /// Argument information for `Demo::test1()`. pub(crate) const ARGINFO_TEST1: [zend_internal_arg_info; 2] = [ diff --git a/crates/stub-parser/tests/compile_test.rs b/crates/stub-parser/tests/compile_test.rs index ba60fc3..ff26445 100644 --- a/crates/stub-parser/tests/compile_test.rs +++ b/crates/stub-parser/tests/compile_test.rs @@ -33,7 +33,7 @@ fn verify_directory(data_dir: &Path) -> Result<(), Box> { // There isn't a way to check that library code compiles, need to make // things into valid binary targets, i.e. with main functions let content_to_compile = - format!("#![allow(unused_imports, non_snake_case)]\n\n{content}\n\nfn main() {{}}"); + format!("#![allow(non_snake_case)]\n\n{content}\n\nfn main() {{}}"); let generated_content_file = tmp_dir.path().join(arginfo_name); fs::write(&generated_content_file, content_to_compile)?; } diff --git a/crates/stub-parser/tests/verification-cases/functions.expected b/crates/stub-parser/tests/verification-cases/functions.expected index 58a32a8..c29822b 100644 --- a/crates/stub-parser/tests/verification-cases/functions.expected +++ b/crates/stub-parser/tests/verification-cases/functions.expected @@ -1,6 +1,5 @@ -use rsphp_macros_c::*; -use rsphp_macros_rs::*; -use rsphp_sys::*; +use rsphp_macros_rs::ZEND_ARG_INFO_FOR_FUNCTION_WITH_RETURN_TYPE_MASK; +use rsphp_sys::{MAY_BE_VOID, zend_internal_arg_info}; /// Argument information for `test1()`. pub(crate) const ARGINFO_TEST1: [zend_internal_arg_info; 1] = [ diff --git a/crates/stub-parser/tests/verification-cases/interface_registration.expected b/crates/stub-parser/tests/verification-cases/interface_registration.expected index a276d45..baf4fcd 100644 --- a/crates/stub-parser/tests/verification-cases/interface_registration.expected +++ b/crates/stub-parser/tests/verification-cases/interface_registration.expected @@ -1,6 +1,5 @@ -use rsphp_macros_c::*; -use rsphp_macros_rs::*; -use rsphp_sys::*; +#[cfg(debug_assertions)] +use rsphp_sys::{_ZEND_TYPE_LIST_BIT, _ZEND_TYPE_LITERAL_NAME_BIT, _ZEND_TYPE_NAME_BIT, zend_function_entry, zend_internal_arg_info, zend_string, zend_type, zend_type_list}; #[cfg(debug_assertions)] use std::ffi::{c_char, CStr}; @@ -210,8 +209,7 @@ fn verify_registration( } /// Module for `CustomCasting\CastableTarget` enum. pub(crate) mod CustomCasting_CastableTarget { - use rsphp_macros_c::*; - use rsphp_sys::*; + use rsphp_sys::{IS_UNDEF, zend_class_entry, zend_enum_add_case_cstr, zend_register_internal_enum}; /// Register the `CustomCasting\CastableTarget` enum. @@ -235,9 +233,11 @@ pub(crate) mod CustomCasting_CastableTarget { } /// Module for `CustomCasting\HasCustomCast` interface. pub(crate) mod CustomCasting_HasCustomCast { - use rsphp_macros_c::*; - use rsphp_macros_rs::*; - use rsphp_sys::*; + use rsphp_macros_c::{INIT_NS_CLASS_ENTRY, ZEND_ARG_OBJ_INFO}; + use rsphp_macros_rs::ZEND_ARG_INFO_FOR_FUNCTION; + use rsphp_sys::{zend_class_entry, zend_function_entry, zend_internal_arg_info, zend_register_internal_interface}; + #[cfg(debug_assertions)] + use rsphp_sys::{ZEND_ACC_ABSTRACT, ZEND_ACC_PUBLIC}; #[cfg(debug_assertions)] use super::verify_registration; diff --git a/crates/stub-parser/tests/verification-cases/method_attribs.expected b/crates/stub-parser/tests/verification-cases/method_attribs.expected index 0ef122c..3f8df10 100644 --- a/crates/stub-parser/tests/verification-cases/method_attribs.expected +++ b/crates/stub-parser/tests/verification-cases/method_attribs.expected @@ -1,6 +1,5 @@ -use rsphp_macros_c::*; -use rsphp_macros_rs::*; -use rsphp_sys::*; +#[cfg(debug_assertions)] +use rsphp_sys::{_ZEND_TYPE_LIST_BIT, _ZEND_TYPE_LITERAL_NAME_BIT, _ZEND_TYPE_NAME_BIT, zend_function_entry, zend_internal_arg_info, zend_string, zend_type, zend_type_list}; #[cfg(debug_assertions)] use std::ffi::{c_char, CStr}; @@ -210,9 +209,11 @@ fn verify_registration( } /// Module for `Demo` class. pub(crate) mod Demo { - use rsphp_macros_c::*; - use rsphp_macros_rs::*; - use rsphp_sys::*; + use rsphp_macros_c::{INIT_CLASS_ENTRY, ZSTR_KNOWN, ZSTR_LEN, ZVAL_STR}; + use rsphp_macros_rs::ZEND_ARG_INFO_FOR_FUNCTION_WITH_RETURN_TYPE_MASK; + use rsphp_sys::{HashTable, MAY_BE_VOID, _zend_known_string_id, zend_add_function_attribute, zend_attribute, zend_class_entry, zend_function, zend_function_entry, zend_hash_str_find_ptr, zend_internal_arg_info, zend_register_internal_class_with_flags, zend_string_init, zval}; + #[cfg(debug_assertions)] + use rsphp_sys::{ZEND_ACC_DEPRECATED, ZEND_ACC_PUBLIC}; #[cfg(debug_assertions)] use super::verify_registration; diff --git a/crates/stub-parser/tests/verification-cases/methods.expected b/crates/stub-parser/tests/verification-cases/methods.expected index ac00339..a6a7acf 100644 --- a/crates/stub-parser/tests/verification-cases/methods.expected +++ b/crates/stub-parser/tests/verification-cases/methods.expected @@ -1,6 +1,5 @@ -use rsphp_macros_c::*; -use rsphp_macros_rs::*; -use rsphp_sys::*; +#[cfg(debug_assertions)] +use rsphp_sys::{_ZEND_TYPE_LIST_BIT, _ZEND_TYPE_LITERAL_NAME_BIT, _ZEND_TYPE_NAME_BIT, zend_function_entry, zend_internal_arg_info, zend_string, zend_type, zend_type_list}; #[cfg(debug_assertions)] use std::ffi::{c_char, CStr}; @@ -210,9 +209,11 @@ fn verify_registration( } /// Module for `Demo` class. pub(crate) mod Demo { - use rsphp_macros_c::*; - use rsphp_macros_rs::*; - use rsphp_sys::*; + use rsphp_macros_c::INIT_CLASS_ENTRY; + use rsphp_macros_rs::ZEND_ARG_INFO_FOR_FUNCTION_WITH_RETURN_TYPE_MASK; + use rsphp_sys::{MAY_BE_VOID, zend_class_entry, zend_function_entry, zend_internal_arg_info, zend_register_internal_class_with_flags}; + #[cfg(debug_assertions)] + use rsphp_sys::{ZEND_ACC_PRIVATE, ZEND_ACC_PROTECTED, ZEND_ACC_PUBLIC, ZEND_ACC_STATIC}; #[cfg(debug_assertions)] use super::verify_registration; diff --git a/crates/stub-parser/tests/verification-cases/namespaced_semi.expected b/crates/stub-parser/tests/verification-cases/namespaced_semi.expected index 1eb37ea..48bdf72 100644 --- a/crates/stub-parser/tests/verification-cases/namespaced_semi.expected +++ b/crates/stub-parser/tests/verification-cases/namespaced_semi.expected @@ -1,6 +1,7 @@ -use rsphp_macros_c::*; -use rsphp_macros_rs::*; -use rsphp_sys::*; +use rsphp_macros_rs::ZEND_ARG_INFO_FOR_FUNCTION_WITH_RETURN_TYPE_MASK; +use rsphp_sys::{MAY_BE_VOID, zend_internal_arg_info}; +#[cfg(debug_assertions)] +use rsphp_sys::{_ZEND_TYPE_LIST_BIT, _ZEND_TYPE_LITERAL_NAME_BIT, _ZEND_TYPE_NAME_BIT, zend_function_entry, zend_string, zend_type, zend_type_list}; #[cfg(debug_assertions)] use std::ffi::{c_char, CStr}; @@ -210,9 +211,11 @@ fn verify_registration( } /// Module for `Demo\InDemoNamespace` class. pub(crate) mod Demo_InDemoNamespace { - use rsphp_macros_c::*; - use rsphp_macros_rs::*; - use rsphp_sys::*; + use rsphp_macros_c::INIT_NS_CLASS_ENTRY; + use rsphp_macros_rs::ZEND_ARG_INFO_FOR_FUNCTION; + use rsphp_sys::{zend_class_entry, zend_function_entry, zend_internal_arg_info, zend_register_internal_class_with_flags}; + #[cfg(debug_assertions)] + use rsphp_sys::ZEND_ACC_PUBLIC; #[cfg(debug_assertions)] use super::verify_registration; diff --git a/crates/stub-parser/tests/verification-cases/no_methods.expected b/crates/stub-parser/tests/verification-cases/no_methods.expected index 4c165de..46640f7 100644 --- a/crates/stub-parser/tests/verification-cases/no_methods.expected +++ b/crates/stub-parser/tests/verification-cases/no_methods.expected @@ -1,11 +1,8 @@ -use rsphp_macros_c::*; -use rsphp_macros_rs::*; -use rsphp_sys::*; /// Module for `Example` class. pub(crate) mod Example { - use rsphp_macros_c::*; - use rsphp_sys::*; + use rsphp_macros_c::{INIT_CLASS_ENTRY, ZEND_TYPE_INIT_MASK, ZVAL_UNDEF}; + use rsphp_sys::{MAY_BE_STRING, ZEND_ACC_FINAL, ZEND_ACC_NOT_SERIALIZABLE, ZEND_ACC_NO_DYNAMIC_PROPERTIES, ZEND_ACC_PUBLIC, zend_class_entry, zend_declare_typed_property, zend_register_internal_class_with_flags, zend_string, zend_string_init, zend_string_release, zval}; /// Register the `Example` class. @@ -71,8 +68,7 @@ pub(crate) mod Example { } /// Module for `MyBoolean` enum. pub(crate) mod MyBoolean { - use rsphp_macros_c::*; - use rsphp_sys::*; + use rsphp_sys::{IS_UNDEF, zend_class_entry, zend_enum_add_case_cstr, zend_register_internal_enum}; /// Register the `MyBoolean` enum. @@ -95,8 +91,8 @@ pub(crate) mod MyBoolean { } /// Module for `Marker` interface. pub(crate) mod Marker { - use rsphp_macros_c::*; - use rsphp_sys::*; + use rsphp_macros_c::INIT_CLASS_ENTRY; + use rsphp_sys::{zend_class_entry, zend_register_internal_interface}; /// Register the `Marker` interface. From 667a945ce954329549558176e972c4978a19e9bc Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Mon, 12 Oct 2026 13:29:41 -0700 Subject: [PATCH 05/12] Stub parser: add `ArgInfoBuilder::scope` property Rather than passing around a scope object everywhere, save it in the overall struct, and replace it when changing scopes --- crates/stub-parser/src/builder.rs | 263 +++++++++++++++--------------- 1 file changed, 134 insertions(+), 129 deletions(-) diff --git a/crates/stub-parser/src/builder.rs b/crates/stub-parser/src/builder.rs index 90d35b5..c7ea0ec 100644 --- a/crates/stub-parser/src/builder.rs +++ b/crates/stub-parser/src/builder.rs @@ -396,8 +396,9 @@ pub(crate) struct ArgInfoBuilder<'program> { /// then there is nothing to verify and the verification code doesn't need /// to be added. needs_verification: bool, - /// File-level imports used. - file_uses: ImportsUsed, + /// Imports used in the current level - replace this while in a different + /// scope. + scope: ImportsUsed, } impl<'program> ArgInfoBuilder<'program> { @@ -410,16 +411,11 @@ impl<'program> ArgInfoBuilder<'program> { namespace: None, verification, needs_verification: false, - file_uses: ImportsUsed::new(), + scope: ImportsUsed::new(), } } } -#[expect( - clippy::unused_self, - clippy::needless_pass_by_ref_mut, - reason = "Non-wildcard imports not set up yet" -)] #[expect( unused_results, reason = "HashSet used to prevent duplication, just need to include the import" @@ -440,12 +436,23 @@ impl ArgInfoBuilder<'_> { for stmt in statements { match &stmt.kind { StmtKind::Function(function) => self.function(function.as_ref())?, - StmtKind::Class(class) => self.class_like(&ClassLikeType::Class(*class.clone()))?, + StmtKind::Class(class) => { + let old_scope = std::mem::take(&mut self.scope); + let result = self.class_like(&ClassLikeType::Class(*class.clone())); + self.scope = old_scope; + result?; + } StmtKind::Enum(php_enum) => { - self.class_like(&ClassLikeType::PhpEnum(*php_enum.clone()))?; + let old_scope = std::mem::take(&mut self.scope); + let result = self.class_like(&ClassLikeType::PhpEnum(*php_enum.clone())); + self.scope = old_scope; + result?; } StmtKind::Interface(interface) => { - self.class_like(&ClassLikeType::Interface(*interface.clone()))?; + let old_scope = std::mem::take(&mut self.scope); + let result = self.class_like(&ClassLikeType::Interface(*interface.clone())); + self.scope = old_scope; + result?; } StmtKind::Namespace(namespace) => self.namespace(namespace.as_ref())?, _ => (), @@ -479,7 +486,7 @@ impl ArgInfoBuilder<'_> { Ok(()) } /// Generate code for a function parameter. - fn param(&self, scope: &mut ImportsUsed, param: &Param) -> Result { + fn param(&mut self, param: &Param) -> Result { let by_ref = u8::from(param.by_ref); let param_type_info_opt = param.type_hint.as_ref().map(|type_hint| { ArgInfoTypeRepr::try_from(type_hint.kind.clone(), self.namespace.as_ref()) @@ -519,7 +526,7 @@ impl ArgInfoBuilder<'_> { format!("({by_ref}, \"{name}\", \"{dflt}\")"), ), }; - scope.import_macros_c.insert(macro_name.clone()); + self.scope.import_macros_c.insert(macro_name.clone()); return Ok(format!(" {macro_name}!{macro_args},\n")); }; let (macro_name, macro_args) = match ( @@ -536,13 +543,13 @@ impl ArgInfoBuilder<'_> { } ((None, Some(param_mask), param_info, nullable), None) => { if param_mask.contains('|') { - scope.import_sys.extend(param_type_info.mask_uses); + self.scope.import_sys.extend(param_type_info.mask_uses); ( "ZEND_ARG_TYPE_MASK".to_owned(), format!("({by_ref}, \"{name}\", {param_mask}, std::ptr::null())"), ) } else { - scope.import_sys.extend(param_type_info.return_uses); + self.scope.import_sys.extend(param_type_info.return_uses); ( format!("ZEND_ARG_{maybe_variadic}TYPE_INFO"), format!( @@ -561,7 +568,7 @@ impl ArgInfoBuilder<'_> { ), ), ((Some(param_class), Some(param_mask), _, _), dflt) => { - scope.import_sys.extend(param_type_info.mask_uses); + self.scope.import_sys.extend(param_type_info.mask_uses); ( format!("ZEND_ARG_{maybe_variadic}OBJ_TYPE_MASK"), format!( @@ -574,7 +581,7 @@ impl ArgInfoBuilder<'_> { ) } ((None, _, Some(param_info), nullable), Some(dflt)) => { - scope.import_sys.extend(param_type_info.return_uses); + self.scope.import_sys.extend(param_type_info.return_uses); ( "ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE".to_owned(), format!( @@ -592,15 +599,13 @@ impl ArgInfoBuilder<'_> { ), _ => return Err(format!("Unhandled parameter: {param:?}")), }; - scope.import_macros_c.insert(macro_name.clone()); + self.scope.import_macros_c.insert(macro_name.clone()); Ok(format!(" {macro_name}!{macro_args},\n")) } /// Generate code for a global function or class method. - #[expect(clippy::too_many_arguments, reason = "To be refactored")] fn build_function( - &self, - scope: &mut ImportsUsed, + &mut self, params: &[Param], comment_opt: Option<&Comment>, name_constant: &str, @@ -616,12 +621,14 @@ impl ArgInfoBuilder<'_> { } let param_code: Vec = params .iter() - .map(|parameter| self.param(scope, parameter)) + .map(|parameter| self.param(parameter)) .collect::, _>>()?; let return_reference_int: u8 = u8::from(return_reference); let Some(return_info) = return_type else { - scope.import_sys.insert("zend_internal_arg_info".to_owned()); - scope + self.scope + .import_sys + .insert("zend_internal_arg_info".to_owned()); + self.scope .import_macros_rs .insert("ZEND_ARG_INFO_FOR_FUNCTION".to_owned()); return Ok(format!( @@ -659,7 +666,7 @@ impl ArgInfoBuilder<'_> { ), ), (None, _, Some(info)) if typehint.is_nullable => { - scope.import_sys.extend(typehint.return_uses); + self.scope.import_sys.extend(typehint.return_uses); ( "ZEND_ARG_INFO_FOR_FUNCTION_WITH_RETURN_TYPE_INFO", format!( @@ -669,7 +676,7 @@ impl ArgInfoBuilder<'_> { ) } (None, Some(mask), _) => { - scope.import_sys.extend(typehint.mask_uses); + self.scope.import_sys.extend(typehint.mask_uses); ( "ZEND_ARG_INFO_FOR_FUNCTION_WITH_RETURN_TYPE_MASK", format!( @@ -679,7 +686,7 @@ impl ArgInfoBuilder<'_> { ) } (Some(class), Some(mask), _) => { - scope.import_sys.extend(typehint.mask_uses); + self.scope.import_sys.extend(typehint.mask_uses); ( "ZEND_ARG_INFO_FOR_FUNCTION_WITH_RETURN_OBJ_TYPE_INFO", format!( @@ -690,8 +697,12 @@ impl ArgInfoBuilder<'_> { } _ => return Err(format!("Unhandled return info: {return_info:?}")), }; - scope.import_sys.insert("zend_internal_arg_info".to_owned()); - scope.import_macros_rs.insert(fn_arginfo_macro.to_owned()); + self.scope + .import_sys + .insert("zend_internal_arg_info".to_owned()); + self.scope + .import_macros_rs + .insert(fn_arginfo_macro.to_owned()); Ok(format!( "/// Argument information for `{doc_name}()`.\ \npub(crate) const {name_constant}: [zend_internal_arg_info; {}] = [\ @@ -714,10 +725,7 @@ impl ArgInfoBuilder<'_> { Some(ns) => format!("{ns}\\{fn_name}"), None => fn_name.to_string(), }; - // Can't borrow from self.file_uses as mutable while calling a function - let mut file_uses = std::mem::take(&mut self.file_uses); let fn_result = self.build_function( - &mut file_uses, &func.params, func.doc_comment.as_ref(), &format!("ARGINFO_{ns_segment}{}", fn_name.to_uppercase()), @@ -725,19 +733,14 @@ impl ArgInfoBuilder<'_> { func.return_type.as_ref(), &doc_name, ); - self.file_uses = file_uses; self.parts.push(fn_result?); Ok(()) } /// Generate code for a class property. - fn build_property( - &self, - scope: &mut ImportsUsed, - prop: &PropertyDecl, - ) -> Result { - scope.import_sys.insert("zval".to_owned()); - scope.import_sys.insert("zend_class_entry".to_owned()); + fn build_property(&mut self, prop: &PropertyDecl) -> Result { + self.scope.import_sys.insert("zval".to_owned()); + self.scope.import_sys.insert("zend_class_entry".to_owned()); let prop_name = prop.name.as_ref().ok_or("Property name missing")?; let prop_default_create = format!( "{space}let mut property_{name}_default_value: zval = unsafe {{ std::mem::zeroed() }};", @@ -776,14 +779,14 @@ impl ArgInfoBuilder<'_> { kind: ExprKind::Identifier(ident), span: _, }) if **ident == *"E_ERROR" => { - scope.import_sys.insert("E_ERROR".to_owned()); + self.scope.import_sys.insert("E_ERROR".to_owned()); ("ZVAL_LONG", Some("E_ERROR".to_owned()), None) } Some(Expr { kind: ExprKind::String(string), span: _, }) => { - scope.import_sys.insert("zend_string_init".to_owned()); + self.scope.import_sys.insert("zend_string_init".to_owned()); ( "ZVAL_INTERNED_STR", Some(format!("property_{prop_name}_default_str")), @@ -797,7 +800,7 @@ impl ArgInfoBuilder<'_> { } _ => return Err(format!("Unhandled property default: {prop:?}")), }; - scope.import_macros_c.insert(init_macro.to_owned()); + self.scope.import_macros_c.insert(init_macro.to_owned()); let prop_name_init = format!( "{space}let property_{name}_name: *mut zend_string = unsafe {{\ \n{space}{space}zend_string_init(\ @@ -809,18 +812,18 @@ impl ArgInfoBuilder<'_> { name = prop_name, space = " " ); - scope.import_sys.insert("zend_string".to_owned()); - scope.import_sys.insert("zend_string_init".to_owned()); + self.scope.import_sys.insert("zend_string".to_owned()); + self.scope.import_sys.insert("zend_string_init".to_owned()); let vis = match prop.visibility { None | Some(Visibility::Public) => "ZEND_ACC_PUBLIC", Some(Visibility::Protected) => "ZEND_ACC_PROTECTED", Some(Visibility::Private) => "ZEND_ACC_PRIVATE", }; - scope.import_sys.insert(vis.to_owned()); + self.scope.import_sys.insert(vis.to_owned()); let (type_declaration, type_code) = match prop.type_hint.as_ref() { Some(type_hint) => { ArgInfoTypeRepr::try_from(type_hint.kind.clone(), self.namespace.as_ref())? - .property_type_code(scope, self.namespace.as_ref(), prop_name)? + .property_type_code(&mut self.scope, self.namespace.as_ref(), prop_name)? } None => (None, "ZEND_TYPE_INIT_NONE!(0)".to_owned()), }; @@ -848,10 +851,12 @@ impl ArgInfoBuilder<'_> { name = prop_name, space = " " ); - scope + self.scope .import_sys .insert("zend_declare_typed_property".to_owned()); - scope.import_sys.insert("zend_string_release".to_owned()); + self.scope + .import_sys + .insert("zend_string_release".to_owned()); let mut parts = vec![prop_default_create]; match init_extra_code { None => (), @@ -875,7 +880,6 @@ impl ArgInfoBuilder<'_> { /// Generate code for a class, enum, or interface declaration. fn class_like(&mut self, wrapped: &ClassLikeType) -> Result<(), String> { - let mut scope = ImportsUsed::new(); let class_name = wrapped.name(); let mut properties = vec![]; let mut constants = vec![]; @@ -901,7 +905,6 @@ impl ArgInfoBuilder<'_> { let fn_name = method.name.as_ref().expect("No errors"); let name_constant = format!("ARGINFO_{}", fn_name.to_uppercase()); module_parts.push(self.build_function( - &mut scope, &method.params, method.doc_comment.as_ref(), &name_constant, @@ -919,7 +922,6 @@ impl ArgInfoBuilder<'_> { ); for attrib in &method.attributes { attribute_setup.push(self.build_method_attribute( - &mut scope, fn_name, attrib, &mut verification, @@ -929,17 +931,13 @@ impl ArgInfoBuilder<'_> { self.needs_verification = true; } ClassLikeMember::Property(property) => { - properties.push(self.build_property(&mut scope, &property)?); + properties.push(self.build_property(&property)?); } ClassLikeMember::ClassConst(class_const) => { - constants.push(self.build_constant(&mut scope, &class_const)?); + constants.push(self.build_constant(&class_const)?); } ClassLikeMember::EnumCase(enum_case) => { - enum_cases.push(self.build_case( - &mut scope, - &enum_case, - enum_backing.as_ref(), - )?); + enum_cases.push(self.build_case(&enum_case, enum_backing.as_ref())?); } } } @@ -947,7 +945,7 @@ impl ArgInfoBuilder<'_> { let mut class_attributes = wrapped .attributes() .iter() - .map(|attribute| self.class_attribute(&mut scope, attribute)) + .map(|attribute| self.class_attribute(attribute)) .collect::, _>>()?; class_attributes.extend(attribute_setup); let class_attributes_str = if class_attributes.is_empty() { @@ -979,7 +977,9 @@ impl ArgInfoBuilder<'_> { registration_params.push(format!("{interface}: *mut zend_class_entry")); } if !method_verifications.is_empty() { - scope.import_sys.insert("zend_function_entry".to_owned()); + self.scope + .import_sys + .insert("zend_function_entry".to_owned()); registration_params.push("methods: &[zend_function_entry]".to_owned()); } // Registration function @@ -1010,7 +1010,7 @@ impl ArgInfoBuilder<'_> { methods_param = registration_params.join(", "), module_parts = module_parts.join("\n"), init_and_register_code = wrapped.init_and_register_code( - &mut scope, + &mut self.scope, self.namespace.as_ref(), method_verifications.len(), self.program @@ -1026,10 +1026,10 @@ impl ArgInfoBuilder<'_> { match self.verification { VerificationMode::Never => (), VerificationMode::Always => { - scope.import_sys.extend(flags_in_verification); + self.scope.import_sys.extend(flags_in_verification); } VerificationMode::DebugAssert => { - let extra_flags_in_verification = &flags_in_verification - &scope.import_sys; + let extra_flags_in_verification = &flags_in_verification - &self.scope.import_sys; if let Some(extra_use) = ImportsUsed::as_use_single(&extra_flags_in_verification, "rsphp_sys") { @@ -1038,7 +1038,7 @@ impl ArgInfoBuilder<'_> { } } } - module_content = scope.as_uses() + &module_content; + module_content = self.scope.as_uses() + &module_content; let module_content_indented = module_content .split('\n') .map(|line| { @@ -1067,8 +1067,7 @@ impl ArgInfoBuilder<'_> { /// Generate code for registering an attribute on a class method. fn build_method_attribute( - &self, - scope: &mut ImportsUsed, + &mut self, name: &str, attrib: &Attribute, verification: &mut VerificationData, @@ -1093,11 +1092,13 @@ impl ArgInfoBuilder<'_> { "message" => "_zend_known_string_id::ZEND_STR_MESSAGE", _ => return Err(format!("Unhandled attribute parameter name: {name}")), }; - scope.import_sys.insert("_zend_known_string_id".to_owned()); + self.scope + .import_sys + .insert("_zend_known_string_id".to_owned()); let init = format!( "(*(*attrib).args.as_mut_ptr().add({index})).name = ZSTR_KNOWN!({known_name});", ); - scope.import_macros_c.insert("ZSTR_KNOWN".to_owned()); + self.scope.import_macros_c.insert("ZSTR_KNOWN".to_owned()); param_init.push(init); } match &arg @@ -1111,13 +1112,13 @@ impl ArgInfoBuilder<'_> { "let arg_{index}_value = zend_string_init(c\"{string}\".as_ptr(), \ \"{string}\".len(), true);", ); - scope.import_sys.insert("zend_string_init".to_owned()); + self.scope.import_sys.insert("zend_string_init".to_owned()); param_init.push(alloc); let param_use = format!( "ZVAL_STR!(std::ptr::from_mut::(&mut (*(*attrib).args.as_mut_ptr().add({index})).value), arg_{index}_value);", ); - scope.import_sys.insert("zval".to_owned()); - scope.import_macros_c.insert("ZVAL_STR".to_owned()); + self.scope.import_sys.insert("zval".to_owned()); + self.scope.import_macros_c.insert("ZVAL_STR".to_owned()); param_init.push(param_use); } _ => return Err(format!("Unhandled attribute value: {arg:#?}")), @@ -1130,9 +1131,11 @@ impl ArgInfoBuilder<'_> { #[expect(clippy::single_match_else, reason = "Clearer as a match")] let (name_alloc, name_use, name_free) = match attrib_name.as_str() { "Deprecated" => { - scope.import_macros_c.insert("ZSTR_LEN".to_owned()); - scope.import_macros_c.insert("ZSTR_KNOWN".to_owned()); - scope.import_sys.insert("_zend_known_string_id".to_owned()); + self.scope.import_macros_c.insert("ZSTR_LEN".to_owned()); + self.scope.import_macros_c.insert("ZSTR_KNOWN".to_owned()); + self.scope + .import_sys + .insert("_zend_known_string_id".to_owned()); ( "\n assert_eq!(10, ZSTR_LEN!(ZSTR_KNOWN!(_zend_known_string_id::ZEND_STR_DEPRECATED_CAPITALIZED)));".to_owned(), "ZSTR_KNOWN!(_zend_known_string_id::ZEND_STR_DEPRECATED_CAPITALIZED)".to_owned(), @@ -1140,11 +1143,13 @@ impl ArgInfoBuilder<'_> { ) } _ => { - scope.import_sys.insert("zend_string".to_owned()); - scope + self.scope.import_sys.insert("zend_string".to_owned()); + self.scope .import_sys .insert("zend_string_init_interned".to_owned()); - scope.import_sys.insert("zend_string_release_ex".to_owned()); + self.scope + .import_sys + .insert("zend_string_release_ex".to_owned()); ( format!( "\n{space}{space}let attrib_name: *mut zend_string = (zend_string_init_interned.expect(\"zend_string_init_interned should be set\"))(\ @@ -1159,11 +1164,13 @@ impl ArgInfoBuilder<'_> { ) } }; - scope.import_sys.insert("zend_function".to_owned()); - scope.import_sys.insert("zend_hash_str_find_ptr".to_owned()); - scope.import_sys.insert("HashTable".to_owned()); - scope.import_sys.insert("zend_attribute".to_owned()); - scope + self.scope.import_sys.insert("zend_function".to_owned()); + self.scope + .import_sys + .insert("zend_hash_str_find_ptr".to_owned()); + self.scope.import_sys.insert("HashTable".to_owned()); + self.scope.import_sys.insert("zend_attribute".to_owned()); + self.scope .import_sys .insert("zend_add_function_attribute".to_owned()); Ok(format!( @@ -1192,11 +1199,7 @@ impl ArgInfoBuilder<'_> { } /// Generate code for registering a class constant. - fn build_constant( - &self, - scope: &mut ImportsUsed, - constant: &ClassConstDecl, - ) -> Result { + fn build_constant(&mut self, constant: &ClassConstDecl) -> Result { let constant_name = constant .name .as_ref() @@ -1206,7 +1209,7 @@ impl ArgInfoBuilder<'_> { Some(Visibility::Protected) => "ZEND_ACC_PROTECTED", Some(Visibility::Private) => "ZEND_ACC_PRIVATE", }; - scope.import_sys.insert(vis.to_owned()); + self.scope.import_sys.insert(vis.to_owned()); let type_repr = ArgInfoTypeRepr::try_from( constant.type_hint.as_ref().map_or_else( || { @@ -1230,15 +1233,18 @@ impl ArgInfoBuilder<'_> { )?, self.namespace.as_ref(), )?; - let (_type_declaration, type_code) = - type_repr.property_type_code(scope, self.namespace.as_ref(), constant_name)?; + let (_type_declaration, type_code) = type_repr.property_type_code( + &mut self.scope, + self.namespace.as_ref(), + constant_name, + )?; let init_code = match &constant.value.kind { ExprKind::Int(num) => { - scope.import_macros_c.insert("ZVAL_LONG".to_owned()); + self.scope.import_macros_c.insert("ZVAL_LONG".to_owned()); format!("ZVAL_LONG!(&mut value, {num});") } ExprKind::Null => { - scope.import_macros_c.insert("ZVAL_NULL".to_owned()); + self.scope.import_macros_c.insert("ZVAL_NULL".to_owned()); "ZVAL_NULL!(&mut value);".to_owned() } ExprKind::Identifier(ident) if *ident == "UNKNOWN".into() => { @@ -1258,23 +1264,23 @@ impl ArgInfoBuilder<'_> { .to_owned(); // Might not be something defined in core if cvalue_val.starts_with("ZEND_") || cvalue_val.starts_with("PHP_") { - scope.import_sys.insert(cvalue_val.clone()); + self.scope.import_sys.insert(cvalue_val.clone()); } else { cvalue_val = "crate::".to_owned() + &cvalue_val; } match type_repr { ArgInfoTypeRepr::Builtin(BuiltinType::Int) => { - scope.import_macros_c.insert("ZVAL_LONG".to_owned()); + self.scope.import_macros_c.insert("ZVAL_LONG".to_owned()); format!("ZVAL_LONG!(&mut value, {cvalue_val} as i64);") } ArgInfoTypeRepr::Builtin(BuiltinType::Float) => { - scope.import_macros_c.insert("ZVAL_DOUBLE".to_owned()); + self.scope.import_macros_c.insert("ZVAL_DOUBLE".to_owned()); format!("ZVAL_DOUBLE!(&mut value, {cvalue_val} as f64);") } ArgInfoTypeRepr::Builtin(BuiltinType::String) => { - scope.import_sys.insert("zend_string".to_owned()); - scope.import_sys.insert("zend_string_init".to_owned()); - scope.import_macros_c.insert("ZVAL_STR".to_owned()); + self.scope.import_sys.insert("zend_string".to_owned()); + self.scope.import_sys.insert("zend_string_init".to_owned()); + self.scope.import_macros_c.insert("ZVAL_STR".to_owned()); format!( "let value_str: *mut zend_string = zend_string_init(\ \n{space3}{cvalue_val}.as_ptr().cast::(),\ @@ -1294,15 +1300,17 @@ impl ArgInfoBuilder<'_> { } _ => return Err(format!("Unhandled constant initialization: {constant:#?}")), }; - scope.import_sys.insert("zval".to_owned()); - scope.import_sys.insert("zend_string".to_owned()); - scope + self.scope.import_sys.insert("zval".to_owned()); + self.scope.import_sys.insert("zend_string".to_owned()); + self.scope .import_sys .insert("zend_string_init_interned".to_owned()); - scope + self.scope .import_sys .insert("zend_declare_typed_class_constant".to_owned()); - scope.import_sys.insert("zend_string_release_ex".to_owned()); + self.scope + .import_sys + .insert("zend_string_release_ex".to_owned()); Ok(format!( "{space}unsafe {{\ \n{space}{space}let mut value: zval = std::mem::zeroed();\ @@ -1323,11 +1331,7 @@ impl ArgInfoBuilder<'_> { } /// Generate code for registering an attribute for the overall class/interface/enum. - fn class_attribute( - &self, - scope: &mut ImportsUsed, - attrib: &Attribute, - ) -> Result { + fn class_attribute(&mut self, attrib: &Attribute) -> Result { let attrib_name = attrib .name .parts @@ -1379,9 +1383,9 @@ impl ArgInfoBuilder<'_> { }) .collect::, _>>()?; for flag in &flags { - scope.import_sys.insert(flag.to_string()); + self.scope.import_sys.insert(flag.to_string()); } - scope.import_macros_c.insert("ZVAL_LONG".to_owned()); + self.scope.import_macros_c.insert("ZVAL_LONG".to_owned()); let init = format!( "ZVAL_LONG!(&mut (*attrib).args[{index}].value, {flags} as i64);", flags = flags.join("|") @@ -1391,15 +1395,17 @@ impl ArgInfoBuilder<'_> { return Err(format!("Unsupported class attribute: {attrib:?}")); } } - scope.import_sys.insert("zend_string".to_owned()); - scope + self.scope.import_sys.insert("zend_string".to_owned()); + self.scope .import_sys .insert("zend_string_init_interned".to_owned()); - scope.import_sys.insert("zend_attribute".to_owned()); - scope + self.scope.import_sys.insert("zend_attribute".to_owned()); + self.scope .import_sys .insert("zend_add_class_attribute".to_owned()); - scope.import_sys.insert("zend_string_release_ex".to_owned()); + self.scope + .import_sys + .insert("zend_string_release_ex".to_owned()); Ok(format!( "{space}unsafe {{\ \n{space}{space}let name: *mut zend_string = (zend_string_init_interned.expect(\"zend_string_init_interned should be set\"))(\ @@ -1425,7 +1431,6 @@ impl ArgInfoBuilder<'_> { /// Generate code for registering an enum case. fn build_case( &mut self, - scope: &mut ImportsUsed, case: &EnumCase, backing: Option<&BuiltinType>, ) -> Result { @@ -1438,13 +1443,13 @@ impl ArgInfoBuilder<'_> { } return match &value.kind { ExprKind::String(string) => { - scope.import_sys.insert("zval".to_owned()); - scope.import_sys.insert("zend_string".to_owned()); - scope.import_sys.insert("zend_string_init".to_owned()); - scope + self.scope.import_sys.insert("zval".to_owned()); + self.scope.import_sys.insert("zend_string".to_owned()); + self.scope.import_sys.insert("zend_string_init".to_owned()); + self.scope .import_sys .insert("zend_enum_add_case_cstr".to_owned()); - scope.import_macros_c.insert("ZVAL_STR".to_owned()); + self.scope.import_macros_c.insert("ZVAL_STR".to_owned()); Ok(format!( "{space}{{\ \n{space3}let mut value: zval = std::mem::zeroed();\ @@ -1463,11 +1468,11 @@ impl ArgInfoBuilder<'_> { )) } ExprKind::Int(num) => { - scope.import_sys.insert("zval".to_owned()); - scope + self.scope.import_sys.insert("zval".to_owned()); + self.scope .import_sys .insert("zend_enum_add_case_cstr".to_owned()); - scope.import_macros_c.insert("ZVAL_LONG".to_owned()); + self.scope.import_macros_c.insert("ZVAL_LONG".to_owned()); Ok(format!( "{space}{{\ \n{space3}let mut value: zval = std::mem::zeroed();\ @@ -1483,7 +1488,7 @@ impl ArgInfoBuilder<'_> { _ => Err(format!("Unhandled enum backing: {case:#?}")), }; } - scope + self.scope .import_sys .insert("zend_enum_add_case_cstr".to_owned()); Ok(format!( @@ -1502,7 +1507,7 @@ impl ArgInfoBuilder<'_> { match self.verification { VerificationMode::Never => (), VerificationMode::Always => { - self.file_uses.import_sys.extend( + self.scope.import_sys.extend( verification_imports .iter() .map(|import| (*import).to_owned()), @@ -1513,7 +1518,7 @@ impl ArgInfoBuilder<'_> { .iter() .map(|import| (*import).to_owned()) .collect::>() - - &self.file_uses.import_sys; + - &self.scope.import_sys; if let Some(extra_use) = ImportsUsed::as_use_single(&extra_verification_imports, "rsphp_sys") { @@ -1527,7 +1532,7 @@ impl ArgInfoBuilder<'_> { }; format!( "{}{extra_uses_code}{}\n{}\n", - self.file_uses.as_uses(), + self.scope.as_uses(), verification_code, self.parts.join("\n") ) From d1216a4a1aa88bdfc6ebfd1cb9596e866ad0ea3a Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Mon, 12 Oct 2026 14:01:00 -0700 Subject: [PATCH 06/12] Stub parser: refactor and clean up `ImportsUsed` --- crates/stub-parser/src/builder.rs | 282 ++++++++++-------------------- crates/stub-parser/src/imports.rs | 66 +++++++ crates/stub-parser/src/lib.rs | 3 + crates/stub-parser/src/types.rs | 44 ++--- 4 files changed, 180 insertions(+), 215 deletions(-) create mode 100644 crates/stub-parser/src/imports.rs diff --git a/crates/stub-parser/src/builder.rs b/crates/stub-parser/src/builder.rs index c7ea0ec..fb38532 100644 --- a/crates/stub-parser/src/builder.rs +++ b/crates/stub-parser/src/builder.rs @@ -1,4 +1,5 @@ use crate::VerificationMode; +use crate::imports::ImportsUsed; use crate::types::ArgInfoTypeRepr; use crate::verification::{VERIFICATION_RSPHP_SYS_IMPORTS, VerificationData}; use php_ast::ast::{BuiltinType, Visibility}; @@ -150,7 +151,7 @@ impl ClassLikeType { if interface_ptrs.is_empty() { String::new() } else { - scope.import_sys.insert("zend_class_implements".to_owned()); + scope.add_import("rsphp_sys", "zend_class_implements"); format!( "\n{space}unsafe {{\ \n{space}{space}zend_class_implements(\ @@ -205,22 +206,18 @@ impl ClassLikeType { let mut flags = vec![]; if class.modifiers.is_final { flags.push("ZEND_ACC_FINAL"); - scope.import_sys.insert("ZEND_ACC_FINAL".to_owned()); + scope.add_import("rsphp_sys", "ZEND_ACC_FINAL"); } match class.doc_comment.as_ref() { None => (), Some(comment) => { if comment.text.contains("@strict-properties") { flags.push("ZEND_ACC_NO_DYNAMIC_PROPERTIES"); - scope - .import_sys - .insert("ZEND_ACC_NO_DYNAMIC_PROPERTIES".to_owned()); + scope.add_import("rsphp_sys", "ZEND_ACC_NO_DYNAMIC_PROPERTIES"); } if comment.text.contains("@not-serializable") { flags.push("ZEND_ACC_NOT_SERIALIZABLE"); - scope - .import_sys - .insert("ZEND_ACC_NOT_SERIALIZABLE".to_owned()); + scope.add_import("rsphp_sys", "ZEND_ACC_NOT_SERIALIZABLE"); } } } @@ -229,11 +226,9 @@ impl ClassLikeType { } else { flags.join("|") }; - scope.import_sys.insert("zend_class_entry".to_owned()); - scope - .import_sys - .insert("zend_register_internal_class_with_flags".to_owned()); - scope.import_macros_c.insert(macro_name.to_owned()); + scope.add_import("rsphp_sys", "zend_class_entry"); + scope.add_import("rsphp_sys", "zend_register_internal_class_with_flags"); + scope.add_import("rsphp_macros_c", macro_name); Ok(format!( "\n{space}let mut ce: zend_class_entry = unsafe {{ std::mem::zeroed() }};\ \n{space}{init_macro}\ @@ -274,11 +269,9 @@ impl ClassLikeType { return Err(format!("Invalid backing type: {backing_type:?}")); } }; - scope.import_sys.insert("zend_class_entry".to_owned()); - scope.import_sys.insert(backing_type_code.to_owned()); - scope - .import_sys - .insert("zend_register_internal_enum".to_owned()); + scope.add_import("rsphp_sys", "zend_class_entry"); + scope.add_import("rsphp_sys", backing_type_code); + scope.add_import("rsphp_sys", "zend_register_internal_enum"); Ok(format!( "\n{space}let class_entry: *mut zend_class_entry = unsafe {{\ \n{space}{space}zend_register_internal_enum(\ @@ -292,11 +285,9 @@ impl ClassLikeType { )) } ClassLikeType::Interface(_) => { - scope.import_sys.insert("zend_class_entry".to_owned()); - scope - .import_sys - .insert("zend_register_internal_interface".to_owned()); - scope.import_macros_c.insert(macro_name.to_owned()); + scope.add_import("rsphp_sys", "zend_class_entry"); + scope.add_import("rsphp_sys", "zend_register_internal_interface"); + scope.add_import("rsphp_macros_c", macro_name); Ok(format!( "\n{space}let mut ce: zend_class_entry = unsafe {{ std::mem::zeroed() }};\ \n{space}{init_macro}\ @@ -330,58 +321,6 @@ impl ClassLikeType { } } -/// Tracker for what imports need to be added to a given scope. -#[derive(Default)] -#[expect( - clippy::field_scoped_visibility_modifiers, - clippy::struct_field_names, - reason = "To be refactored" -)] -pub(crate) struct ImportsUsed { - /// Imports needed from `rsphp_sys`. - pub(crate) import_sys: HashSet, - /// Imports needed from `rsphp_macros_c`. - pub(crate) import_macros_c: HashSet, - /// Imports needed from `rsphp_macros_rs`. - pub(crate) import_macros_rs: HashSet, -} - -impl ImportsUsed { - /// Create a new empty set of imports. - fn new() -> Self { - Self { - import_sys: HashSet::new(), - import_macros_c: HashSet::new(), - import_macros_rs: HashSet::new(), - } - } - - /// Format all imports. - fn as_uses(&self) -> String { - vec![ - Self::as_use_single(&self.import_macros_c, "rsphp_macros_c"), - Self::as_use_single(&self.import_macros_rs, "rsphp_macros_rs"), - Self::as_use_single(&self.import_sys, "rsphp_sys"), - ] - .into_iter() - .flatten() - .collect::() - } - - /// Format imports for a single module. - fn as_use_single(used: &HashSet, module: &str) -> Option { - if used.is_empty() { - return None; - } - let mut used_names: Vec = used.clone().into_iter().collect(); - if used_names.len() == 1 { - return Some(format!("use {module}::{};\n", used_names[0])); - } - used_names.sort(); - Some(format!("use {module}::{{{}}};\n", used_names.join(", "))) - } -} - /// Stateful tool for building up argument information for a program. pub(crate) struct ArgInfoBuilder<'program> { /// The program text. @@ -526,7 +465,7 @@ impl ArgInfoBuilder<'_> { format!("({by_ref}, \"{name}\", \"{dflt}\")"), ), }; - self.scope.import_macros_c.insert(macro_name.clone()); + self.scope.add_import("rsphp_macros_c", macro_name.clone()); return Ok(format!(" {macro_name}!{macro_args},\n")); }; let (macro_name, macro_args) = match ( @@ -543,13 +482,13 @@ impl ArgInfoBuilder<'_> { } ((None, Some(param_mask), param_info, nullable), None) => { if param_mask.contains('|') { - self.scope.import_sys.extend(param_type_info.mask_uses); + self.scope.sys.extend(param_type_info.mask_uses); ( "ZEND_ARG_TYPE_MASK".to_owned(), format!("({by_ref}, \"{name}\", {param_mask}, std::ptr::null())"), ) } else { - self.scope.import_sys.extend(param_type_info.return_uses); + self.scope.sys.extend(param_type_info.return_uses); ( format!("ZEND_ARG_{maybe_variadic}TYPE_INFO"), format!( @@ -568,7 +507,7 @@ impl ArgInfoBuilder<'_> { ), ), ((Some(param_class), Some(param_mask), _, _), dflt) => { - self.scope.import_sys.extend(param_type_info.mask_uses); + self.scope.sys.extend(param_type_info.mask_uses); ( format!("ZEND_ARG_{maybe_variadic}OBJ_TYPE_MASK"), format!( @@ -581,7 +520,7 @@ impl ArgInfoBuilder<'_> { ) } ((None, _, Some(param_info), nullable), Some(dflt)) => { - self.scope.import_sys.extend(param_type_info.return_uses); + self.scope.sys.extend(param_type_info.return_uses); ( "ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE".to_owned(), format!( @@ -599,7 +538,7 @@ impl ArgInfoBuilder<'_> { ), _ => return Err(format!("Unhandled parameter: {param:?}")), }; - self.scope.import_macros_c.insert(macro_name.clone()); + self.scope.add_import("rsphp_macros_c", macro_name.clone()); Ok(format!(" {macro_name}!{macro_args},\n")) } @@ -625,12 +564,9 @@ impl ArgInfoBuilder<'_> { .collect::, _>>()?; let return_reference_int: u8 = u8::from(return_reference); let Some(return_info) = return_type else { + self.scope.add_import("rsphp_sys", "zend_internal_arg_info"); self.scope - .import_sys - .insert("zend_internal_arg_info".to_owned()); - self.scope - .import_macros_rs - .insert("ZEND_ARG_INFO_FOR_FUNCTION".to_owned()); + .add_import("rsphp_macros_rs", "ZEND_ARG_INFO_FOR_FUNCTION"); return Ok(format!( "/// Argument information for `{doc_name}()`.\ \npub(crate) const {name_constant}: [zend_internal_arg_info; {}] = [\ @@ -666,7 +602,7 @@ impl ArgInfoBuilder<'_> { ), ), (None, _, Some(info)) if typehint.is_nullable => { - self.scope.import_sys.extend(typehint.return_uses); + self.scope.sys.extend(typehint.return_uses); ( "ZEND_ARG_INFO_FOR_FUNCTION_WITH_RETURN_TYPE_INFO", format!( @@ -676,7 +612,7 @@ impl ArgInfoBuilder<'_> { ) } (None, Some(mask), _) => { - self.scope.import_sys.extend(typehint.mask_uses); + self.scope.sys.extend(typehint.mask_uses); ( "ZEND_ARG_INFO_FOR_FUNCTION_WITH_RETURN_TYPE_MASK", format!( @@ -686,7 +622,7 @@ impl ArgInfoBuilder<'_> { ) } (Some(class), Some(mask), _) => { - self.scope.import_sys.extend(typehint.mask_uses); + self.scope.sys.extend(typehint.mask_uses); ( "ZEND_ARG_INFO_FOR_FUNCTION_WITH_RETURN_OBJ_TYPE_INFO", format!( @@ -697,12 +633,8 @@ impl ArgInfoBuilder<'_> { } _ => return Err(format!("Unhandled return info: {return_info:?}")), }; - self.scope - .import_sys - .insert("zend_internal_arg_info".to_owned()); - self.scope - .import_macros_rs - .insert(fn_arginfo_macro.to_owned()); + self.scope.add_import("rsphp_sys", "zend_internal_arg_info"); + self.scope.add_import("rsphp_macros_rs", fn_arginfo_macro); Ok(format!( "/// Argument information for `{doc_name}()`.\ \npub(crate) const {name_constant}: [zend_internal_arg_info; {}] = [\ @@ -739,8 +671,8 @@ impl ArgInfoBuilder<'_> { /// Generate code for a class property. fn build_property(&mut self, prop: &PropertyDecl) -> Result { - self.scope.import_sys.insert("zval".to_owned()); - self.scope.import_sys.insert("zend_class_entry".to_owned()); + self.scope.add_import("rsphp_sys", "zval"); + self.scope.add_import("rsphp_sys", "zend_class_entry"); let prop_name = prop.name.as_ref().ok_or("Property name missing")?; let prop_default_create = format!( "{space}let mut property_{name}_default_value: zval = unsafe {{ std::mem::zeroed() }};", @@ -779,14 +711,14 @@ impl ArgInfoBuilder<'_> { kind: ExprKind::Identifier(ident), span: _, }) if **ident == *"E_ERROR" => { - self.scope.import_sys.insert("E_ERROR".to_owned()); + self.scope.add_import("rsphp_sys", "E_ERROR"); ("ZVAL_LONG", Some("E_ERROR".to_owned()), None) } Some(Expr { kind: ExprKind::String(string), span: _, }) => { - self.scope.import_sys.insert("zend_string_init".to_owned()); + self.scope.add_import("rsphp_sys", "zend_string_init"); ( "ZVAL_INTERNED_STR", Some(format!("property_{prop_name}_default_str")), @@ -800,7 +732,7 @@ impl ArgInfoBuilder<'_> { } _ => return Err(format!("Unhandled property default: {prop:?}")), }; - self.scope.import_macros_c.insert(init_macro.to_owned()); + self.scope.add_import("rsphp_macros_c", init_macro); let prop_name_init = format!( "{space}let property_{name}_name: *mut zend_string = unsafe {{\ \n{space}{space}zend_string_init(\ @@ -812,14 +744,14 @@ impl ArgInfoBuilder<'_> { name = prop_name, space = " " ); - self.scope.import_sys.insert("zend_string".to_owned()); - self.scope.import_sys.insert("zend_string_init".to_owned()); + self.scope.add_import("rsphp_sys", "zend_string"); + self.scope.add_import("rsphp_sys", "zend_string_init"); let vis = match prop.visibility { None | Some(Visibility::Public) => "ZEND_ACC_PUBLIC", Some(Visibility::Protected) => "ZEND_ACC_PROTECTED", Some(Visibility::Private) => "ZEND_ACC_PRIVATE", }; - self.scope.import_sys.insert(vis.to_owned()); + self.scope.add_import("rsphp_sys", vis); let (type_declaration, type_code) = match prop.type_hint.as_ref() { Some(type_hint) => { ArgInfoTypeRepr::try_from(type_hint.kind.clone(), self.namespace.as_ref())? @@ -852,11 +784,8 @@ impl ArgInfoBuilder<'_> { space = " " ); self.scope - .import_sys - .insert("zend_declare_typed_property".to_owned()); - self.scope - .import_sys - .insert("zend_string_release".to_owned()); + .add_import("rsphp_sys", "zend_declare_typed_property"); + self.scope.add_import("rsphp_sys", "zend_string_release"); let mut parts = vec![prop_default_create]; match init_extra_code { None => (), @@ -977,9 +906,7 @@ impl ArgInfoBuilder<'_> { registration_params.push(format!("{interface}: *mut zend_class_entry")); } if !method_verifications.is_empty() { - self.scope - .import_sys - .insert("zend_function_entry".to_owned()); + self.scope.add_import("rsphp_sys", "zend_function_entry"); registration_params.push("methods: &[zend_function_entry]".to_owned()); } // Registration function @@ -1025,11 +952,15 @@ impl ArgInfoBuilder<'_> { let flags_in_verification = VerificationData::get_all_flags(&method_verifications); match self.verification { VerificationMode::Never => (), - VerificationMode::Always => { - self.scope.import_sys.extend(flags_in_verification); + VerificationMode::Always => + { + #[expect(clippy::iter_over_hash_type, reason = "Order does not matter")] + for flag in flags_in_verification { + self.scope.add_import("rsphp_sys", flag); + } } VerificationMode::DebugAssert => { - let extra_flags_in_verification = &flags_in_verification - &self.scope.import_sys; + let extra_flags_in_verification = &flags_in_verification - &self.scope.sys; if let Some(extra_use) = ImportsUsed::as_use_single(&extra_flags_in_verification, "rsphp_sys") { @@ -1092,13 +1023,11 @@ impl ArgInfoBuilder<'_> { "message" => "_zend_known_string_id::ZEND_STR_MESSAGE", _ => return Err(format!("Unhandled attribute parameter name: {name}")), }; - self.scope - .import_sys - .insert("_zend_known_string_id".to_owned()); + self.scope.add_import("rsphp_sys", "_zend_known_string_id"); let init = format!( "(*(*attrib).args.as_mut_ptr().add({index})).name = ZSTR_KNOWN!({known_name});", ); - self.scope.import_macros_c.insert("ZSTR_KNOWN".to_owned()); + self.scope.add_import("rsphp_macros_c", "ZSTR_KNOWN"); param_init.push(init); } match &arg @@ -1112,13 +1041,13 @@ impl ArgInfoBuilder<'_> { "let arg_{index}_value = zend_string_init(c\"{string}\".as_ptr(), \ \"{string}\".len(), true);", ); - self.scope.import_sys.insert("zend_string_init".to_owned()); + self.scope.add_import("rsphp_sys", "zend_string_init"); param_init.push(alloc); let param_use = format!( "ZVAL_STR!(std::ptr::from_mut::(&mut (*(*attrib).args.as_mut_ptr().add({index})).value), arg_{index}_value);", ); - self.scope.import_sys.insert("zval".to_owned()); - self.scope.import_macros_c.insert("ZVAL_STR".to_owned()); + self.scope.add_import("rsphp_sys", "zval"); + self.scope.add_import("rsphp_macros_c", "ZVAL_STR"); param_init.push(param_use); } _ => return Err(format!("Unhandled attribute value: {arg:#?}")), @@ -1131,11 +1060,9 @@ impl ArgInfoBuilder<'_> { #[expect(clippy::single_match_else, reason = "Clearer as a match")] let (name_alloc, name_use, name_free) = match attrib_name.as_str() { "Deprecated" => { - self.scope.import_macros_c.insert("ZSTR_LEN".to_owned()); - self.scope.import_macros_c.insert("ZSTR_KNOWN".to_owned()); - self.scope - .import_sys - .insert("_zend_known_string_id".to_owned()); + self.scope.add_import("rsphp_macros_c", "ZSTR_LEN"); + self.scope.add_import("rsphp_macros_c", "ZSTR_KNOWN"); + self.scope.add_import("rsphp_sys", "_zend_known_string_id"); ( "\n assert_eq!(10, ZSTR_LEN!(ZSTR_KNOWN!(_zend_known_string_id::ZEND_STR_DEPRECATED_CAPITALIZED)));".to_owned(), "ZSTR_KNOWN!(_zend_known_string_id::ZEND_STR_DEPRECATED_CAPITALIZED)".to_owned(), @@ -1143,13 +1070,10 @@ impl ArgInfoBuilder<'_> { ) } _ => { - self.scope.import_sys.insert("zend_string".to_owned()); - self.scope - .import_sys - .insert("zend_string_init_interned".to_owned()); + self.scope.add_import("rsphp_sys", "zend_string"); self.scope - .import_sys - .insert("zend_string_release_ex".to_owned()); + .add_import("rsphp_sys", "zend_string_init_interned"); + self.scope.add_import("rsphp_sys", "zend_string_release_ex"); ( format!( "\n{space}{space}let attrib_name: *mut zend_string = (zend_string_init_interned.expect(\"zend_string_init_interned should be set\"))(\ @@ -1164,15 +1088,12 @@ impl ArgInfoBuilder<'_> { ) } }; - self.scope.import_sys.insert("zend_function".to_owned()); + self.scope.add_import("rsphp_sys", "zend_function"); + self.scope.add_import("rsphp_sys", "zend_hash_str_find_ptr"); + self.scope.add_import("rsphp_sys", "HashTable"); + self.scope.add_import("rsphp_sys", "zend_attribute"); self.scope - .import_sys - .insert("zend_hash_str_find_ptr".to_owned()); - self.scope.import_sys.insert("HashTable".to_owned()); - self.scope.import_sys.insert("zend_attribute".to_owned()); - self.scope - .import_sys - .insert("zend_add_function_attribute".to_owned()); + .add_import("rsphp_sys", "zend_add_function_attribute"); Ok(format!( "{space}unsafe {{\ \n{space}{space}let fn_ptr: *mut zend_function = zend_hash_str_find_ptr(\ @@ -1209,7 +1130,7 @@ impl ArgInfoBuilder<'_> { Some(Visibility::Protected) => "ZEND_ACC_PROTECTED", Some(Visibility::Private) => "ZEND_ACC_PRIVATE", }; - self.scope.import_sys.insert(vis.to_owned()); + self.scope.add_import("rsphp_sys", vis); let type_repr = ArgInfoTypeRepr::try_from( constant.type_hint.as_ref().map_or_else( || { @@ -1240,11 +1161,11 @@ impl ArgInfoBuilder<'_> { )?; let init_code = match &constant.value.kind { ExprKind::Int(num) => { - self.scope.import_macros_c.insert("ZVAL_LONG".to_owned()); + self.scope.add_import("rsphp_macros_c", "ZVAL_LONG"); format!("ZVAL_LONG!(&mut value, {num});") } ExprKind::Null => { - self.scope.import_macros_c.insert("ZVAL_NULL".to_owned()); + self.scope.add_import("rsphp_macros_c", "ZVAL_NULL"); "ZVAL_NULL!(&mut value);".to_owned() } ExprKind::Identifier(ident) if *ident == "UNKNOWN".into() => { @@ -1264,23 +1185,23 @@ impl ArgInfoBuilder<'_> { .to_owned(); // Might not be something defined in core if cvalue_val.starts_with("ZEND_") || cvalue_val.starts_with("PHP_") { - self.scope.import_sys.insert(cvalue_val.clone()); + self.scope.add_import("rsphp_sys", cvalue_val.clone()); } else { cvalue_val = "crate::".to_owned() + &cvalue_val; } match type_repr { ArgInfoTypeRepr::Builtin(BuiltinType::Int) => { - self.scope.import_macros_c.insert("ZVAL_LONG".to_owned()); + self.scope.add_import("rsphp_macros_c", "ZVAL_LONG"); format!("ZVAL_LONG!(&mut value, {cvalue_val} as i64);") } ArgInfoTypeRepr::Builtin(BuiltinType::Float) => { - self.scope.import_macros_c.insert("ZVAL_DOUBLE".to_owned()); + self.scope.add_import("rsphp_macros_c", "ZVAL_DOUBLE"); format!("ZVAL_DOUBLE!(&mut value, {cvalue_val} as f64);") } ArgInfoTypeRepr::Builtin(BuiltinType::String) => { - self.scope.import_sys.insert("zend_string".to_owned()); - self.scope.import_sys.insert("zend_string_init".to_owned()); - self.scope.import_macros_c.insert("ZVAL_STR".to_owned()); + self.scope.add_import("rsphp_sys", "zend_string"); + self.scope.add_import("rsphp_sys", "zend_string_init"); + self.scope.add_import("rsphp_macros_c", "ZVAL_STR"); format!( "let value_str: *mut zend_string = zend_string_init(\ \n{space3}{cvalue_val}.as_ptr().cast::(),\ @@ -1300,17 +1221,13 @@ impl ArgInfoBuilder<'_> { } _ => return Err(format!("Unhandled constant initialization: {constant:#?}")), }; - self.scope.import_sys.insert("zval".to_owned()); - self.scope.import_sys.insert("zend_string".to_owned()); - self.scope - .import_sys - .insert("zend_string_init_interned".to_owned()); + self.scope.add_import("rsphp_sys", "zval"); + self.scope.add_import("rsphp_sys", "zend_string"); self.scope - .import_sys - .insert("zend_declare_typed_class_constant".to_owned()); + .add_import("rsphp_sys", "zend_string_init_interned"); self.scope - .import_sys - .insert("zend_string_release_ex".to_owned()); + .add_import("rsphp_sys", "zend_declare_typed_class_constant"); + self.scope.add_import("rsphp_sys", "zend_string_release_ex"); Ok(format!( "{space}unsafe {{\ \n{space}{space}let mut value: zval = std::mem::zeroed();\ @@ -1383,9 +1300,9 @@ impl ArgInfoBuilder<'_> { }) .collect::, _>>()?; for flag in &flags { - self.scope.import_sys.insert(flag.to_string()); + self.scope.add_import("rsphp_sys", *flag); } - self.scope.import_macros_c.insert("ZVAL_LONG".to_owned()); + self.scope.add_import("rsphp_macros_c", "ZVAL_LONG"); let init = format!( "ZVAL_LONG!(&mut (*attrib).args[{index}].value, {flags} as i64);", flags = flags.join("|") @@ -1395,17 +1312,13 @@ impl ArgInfoBuilder<'_> { return Err(format!("Unsupported class attribute: {attrib:?}")); } } - self.scope.import_sys.insert("zend_string".to_owned()); + self.scope.add_import("rsphp_sys", "zend_string"); self.scope - .import_sys - .insert("zend_string_init_interned".to_owned()); - self.scope.import_sys.insert("zend_attribute".to_owned()); + .add_import("rsphp_sys", "zend_string_init_interned"); + self.scope.add_import("rsphp_sys", "zend_attribute"); self.scope - .import_sys - .insert("zend_add_class_attribute".to_owned()); - self.scope - .import_sys - .insert("zend_string_release_ex".to_owned()); + .add_import("rsphp_sys", "zend_add_class_attribute"); + self.scope.add_import("rsphp_sys", "zend_string_release_ex"); Ok(format!( "{space}unsafe {{\ \n{space}{space}let name: *mut zend_string = (zend_string_init_interned.expect(\"zend_string_init_interned should be set\"))(\ @@ -1443,13 +1356,12 @@ impl ArgInfoBuilder<'_> { } return match &value.kind { ExprKind::String(string) => { - self.scope.import_sys.insert("zval".to_owned()); - self.scope.import_sys.insert("zend_string".to_owned()); - self.scope.import_sys.insert("zend_string_init".to_owned()); + self.scope.add_import("rsphp_sys", "zval"); + self.scope.add_import("rsphp_sys", "zend_string"); + self.scope.add_import("rsphp_sys", "zend_string_init"); self.scope - .import_sys - .insert("zend_enum_add_case_cstr".to_owned()); - self.scope.import_macros_c.insert("ZVAL_STR".to_owned()); + .add_import("rsphp_sys", "zend_enum_add_case_cstr"); + self.scope.add_import("rsphp_macros_c", "ZVAL_STR"); Ok(format!( "{space}{{\ \n{space3}let mut value: zval = std::mem::zeroed();\ @@ -1468,11 +1380,10 @@ impl ArgInfoBuilder<'_> { )) } ExprKind::Int(num) => { - self.scope.import_sys.insert("zval".to_owned()); + self.scope.add_import("rsphp_sys", "zval"); self.scope - .import_sys - .insert("zend_enum_add_case_cstr".to_owned()); - self.scope.import_macros_c.insert("ZVAL_LONG".to_owned()); + .add_import("rsphp_sys", "zend_enum_add_case_cstr"); + self.scope.add_import("rsphp_macros_c", "ZVAL_LONG"); Ok(format!( "{space}{{\ \n{space3}let mut value: zval = std::mem::zeroed();\ @@ -1489,8 +1400,7 @@ impl ArgInfoBuilder<'_> { }; } self.scope - .import_sys - .insert("zend_enum_add_case_cstr".to_owned()); + .add_import("rsphp_sys", "zend_enum_add_case_cstr"); Ok(format!( "{space}zend_enum_add_case_cstr(class_entry, c\"{name}\".as_ptr(), std::ptr::null_mut());", name = case_name, @@ -1507,18 +1417,16 @@ impl ArgInfoBuilder<'_> { match self.verification { VerificationMode::Never => (), VerificationMode::Always => { - self.scope.import_sys.extend( - verification_imports - .iter() - .map(|import| (*import).to_owned()), - ); + for import in verification_imports { + self.scope.add_import("rsphp_sys", *import); + } } VerificationMode::DebugAssert => { let extra_verification_imports = &verification_imports .iter() .map(|import| (*import).to_owned()) .collect::>() - - &self.scope.import_sys; + - &self.scope.sys; if let Some(extra_use) = ImportsUsed::as_use_single(&extra_verification_imports, "rsphp_sys") { diff --git a/crates/stub-parser/src/imports.rs b/crates/stub-parser/src/imports.rs new file mode 100644 index 0000000..d86175a --- /dev/null +++ b/crates/stub-parser/src/imports.rs @@ -0,0 +1,66 @@ +use std::collections::HashSet; + +/// Tracker for what imports need to be added to a given scope. +#[derive(Default)] +#[expect(clippy::field_scoped_visibility_modifiers, reason = "To be refactored")] +pub(crate) struct ImportsUsed { + /// Imports needed from `rsphp_sys`. + pub(crate) sys: HashSet, + /// Imports needed from `rsphp_macros_c`. + macros_c: HashSet, + /// Imports needed from `rsphp_macros_rs`. + macros_rs: HashSet, +} + +impl ImportsUsed { + /// Create a new empty set of imports. + pub(crate) fn new() -> Self { + Self { + sys: HashSet::new(), + macros_c: HashSet::new(), + macros_rs: HashSet::new(), + } + } + + /// Add a new use. + #[expect( + clippy::panic, + reason = "Indicates a bug in the library, surfaced at build time" + )] + pub(crate) fn add_import(&mut self, module: &str, name: T) -> bool + where + T: Into, + { + match module { + "rsphp_sys" => self.sys.insert(name.into()), + "rsphp_macros_c" => self.macros_c.insert(name.into()), + "rsphp_macros_rs" => self.macros_rs.insert(name.into()), + _ => panic!("Invalid use for module {module}"), + } + } + + /// Format all imports. + pub(crate) fn as_uses(&self) -> String { + vec![ + Self::as_use_single(&self.macros_c, "rsphp_macros_c"), + Self::as_use_single(&self.macros_rs, "rsphp_macros_rs"), + Self::as_use_single(&self.sys, "rsphp_sys"), + ] + .into_iter() + .flatten() + .collect::() + } + + /// Format imports for a single module. + pub(crate) fn as_use_single(used: &HashSet, module: &str) -> Option { + if used.is_empty() { + return None; + } + let mut used_names: Vec = used.clone().into_iter().collect(); + if used_names.len() == 1 { + return Some(format!("use {module}::{};\n", used_names[0])); + } + used_names.sort(); + Some(format!("use {module}::{{{}}};\n", used_names.join(", "))) + } +} diff --git a/crates/stub-parser/src/lib.rs b/crates/stub-parser/src/lib.rs index 686c82e..6110234 100644 --- a/crates/stub-parser/src/lib.rs +++ b/crates/stub-parser/src/lib.rs @@ -14,6 +14,9 @@ pub use crate::errors::ErrorCode; mod builder; use crate::builder::ArgInfoBuilder; +/// Module for `ImportsUsed`. +mod imports; + /// Module for `ArgInfoTypeRepr`. mod types; diff --git a/crates/stub-parser/src/types.rs b/crates/stub-parser/src/types.rs index ce91608..747079c 100644 --- a/crates/stub-parser/src/types.rs +++ b/crates/stub-parser/src/types.rs @@ -1,7 +1,7 @@ use php_ast::ast::{BuiltinType, NameKind}; use php_ast::owned::{Name, TypeHintKind}; -use crate::builder::ImportsUsed; +use crate::imports::ImportsUsed; /// Information for a typehint for a function or parameter. #[derive(Debug)] @@ -101,30 +101,24 @@ impl ArgInfoTypeRepr { match self { ArgInfoTypeRepr::Builtin(_) => { let type_info = self.type_info()?; - scope.import_sys.insert(type_info.1.to_owned()); - scope - .import_macros_c - .insert("ZEND_TYPE_INIT_MASK".to_owned()); + scope.add_import("rsphp_sys", type_info.1); + scope.add_import("rsphp_macros_c", "ZEND_TYPE_INIT_MASK"); Ok((None, format!("ZEND_TYPE_INIT_MASK!({})", type_info.1))) } ArgInfoTypeRepr::Nullable(inner) => match **inner { ArgInfoTypeRepr::Builtin(_) => { let type_info = inner.type_info()?; - scope.import_sys.insert(type_info.1.to_owned()); - scope.import_sys.insert("MAY_BE_NULL".to_owned()); - scope - .import_macros_c - .insert("ZEND_TYPE_INIT_MASK".to_owned()); + scope.add_import("rsphp_sys", type_info.1); + scope.add_import("rsphp_sys", "MAY_BE_NULL"); + scope.add_import("rsphp_macros_c", "ZEND_TYPE_INIT_MASK"); Ok(( None, format!("ZEND_TYPE_INIT_MASK!({}|MAY_BE_NULL)", type_info.1), )) } ArgInfoTypeRepr::Named(_) | ArgInfoTypeRepr::FullyQualifiedNamed(_) => { - scope.import_sys.insert("MAY_BE_NULL".to_owned()); - scope - .import_macros_c - .insert("ZEND_TYPE_INIT_CLASS".to_owned()); + scope.add_import("rsphp_sys", "MAY_BE_NULL"); + scope.add_import("rsphp_macros_c", "ZEND_TYPE_INIT_CLASS"); Ok(( inner.property_type_code(scope, namespace, name_str)?.0, format!("ZEND_TYPE_INIT_CLASS!(property_{name_str}_type, 0, MAY_BE_NULL)"), @@ -133,11 +127,9 @@ impl ArgInfoTypeRepr { _ => Err(format!("Unhandled type code: {self:?}")), }, ArgInfoTypeRepr::Named(raw_name) => { - scope.import_sys.insert("zend_string".to_owned()); - scope.import_sys.insert("zend_string_init".to_owned()); - scope - .import_macros_c - .insert("ZEND_TYPE_INIT_CLASS".to_owned()); + scope.add_import("rsphp_sys", "zend_string"); + scope.add_import("rsphp_sys", "zend_string_init"); + scope.add_import("rsphp_macros_c", "ZEND_TYPE_INIT_CLASS"); Ok(( Some(format!( "{space}let property_{name_str}_type: *mut zend_string = unsafe {{\ @@ -154,11 +146,9 @@ impl ArgInfoTypeRepr { )) } ArgInfoTypeRepr::FullyQualifiedNamed(name) => { - scope.import_sys.insert("zend_string".to_owned()); - scope.import_sys.insert("zend_string_init".to_owned()); - scope - .import_macros_c - .insert("ZEND_TYPE_INIT_CLASS".to_owned()); + scope.add_import("rsphp_sys", "zend_string"); + scope.add_import("rsphp_sys", "zend_string_init"); + scope.add_import("rsphp_macros_c", "ZEND_TYPE_INIT_CLASS"); Ok(( Some(format!( "{space}let property_{name_str}_type: *mut zend_string = unsafe {{\ @@ -183,11 +173,9 @@ impl ArgInfoTypeRepr { .map(|inner_type| inner_type.type_info().map(|inner_info| inner_info.1)) .collect::>()?; for inner_flag in &parts { - scope.import_sys.insert(inner_flag.to_string()); + scope.add_import("rsphp_sys", *inner_flag); } - scope - .import_macros_c - .insert("ZEND_TYPE_INIT_MASK".to_owned()); + scope.add_import("rsphp_macros_c", "ZEND_TYPE_INIT_MASK"); Ok((None, format!("ZEND_TYPE_INIT_MASK!({})", parts.join("|")))) } else { Err(format!("Unhandled union: {inner:?}")) From 72cc156717185d0b03f0b3e85079fdf388da8de3 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Mon, 12 Oct 2026 14:14:27 -0700 Subject: [PATCH 07/12] Stub parser: continue `ImportsUsed` refactor --- crates/stub-parser/src/builder.rs | 29 ++++++++++++++++------------- crates/stub-parser/src/imports.rs | 20 ++++++++++++++++++-- 2 files changed, 34 insertions(+), 15 deletions(-) diff --git a/crates/stub-parser/src/builder.rs b/crates/stub-parser/src/builder.rs index fb38532..210b731 100644 --- a/crates/stub-parser/src/builder.rs +++ b/crates/stub-parser/src/builder.rs @@ -482,13 +482,13 @@ impl ArgInfoBuilder<'_> { } ((None, Some(param_mask), param_info, nullable), None) => { if param_mask.contains('|') { - self.scope.sys.extend(param_type_info.mask_uses); + self.scope.extend_sys_imports(param_type_info.mask_uses); ( "ZEND_ARG_TYPE_MASK".to_owned(), format!("({by_ref}, \"{name}\", {param_mask}, std::ptr::null())"), ) } else { - self.scope.sys.extend(param_type_info.return_uses); + self.scope.extend_sys_imports(param_type_info.return_uses); ( format!("ZEND_ARG_{maybe_variadic}TYPE_INFO"), format!( @@ -507,7 +507,7 @@ impl ArgInfoBuilder<'_> { ), ), ((Some(param_class), Some(param_mask), _, _), dflt) => { - self.scope.sys.extend(param_type_info.mask_uses); + self.scope.extend_sys_imports(param_type_info.mask_uses); ( format!("ZEND_ARG_{maybe_variadic}OBJ_TYPE_MASK"), format!( @@ -520,7 +520,7 @@ impl ArgInfoBuilder<'_> { ) } ((None, _, Some(param_info), nullable), Some(dflt)) => { - self.scope.sys.extend(param_type_info.return_uses); + self.scope.extend_sys_imports(param_type_info.return_uses); ( "ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE".to_owned(), format!( @@ -602,7 +602,7 @@ impl ArgInfoBuilder<'_> { ), ), (None, _, Some(info)) if typehint.is_nullable => { - self.scope.sys.extend(typehint.return_uses); + self.scope.extend_sys_imports(typehint.return_uses); ( "ZEND_ARG_INFO_FOR_FUNCTION_WITH_RETURN_TYPE_INFO", format!( @@ -612,7 +612,7 @@ impl ArgInfoBuilder<'_> { ) } (None, Some(mask), _) => { - self.scope.sys.extend(typehint.mask_uses); + self.scope.extend_sys_imports(typehint.mask_uses); ( "ZEND_ARG_INFO_FOR_FUNCTION_WITH_RETURN_TYPE_MASK", format!( @@ -622,7 +622,7 @@ impl ArgInfoBuilder<'_> { ) } (Some(class), Some(mask), _) => { - self.scope.sys.extend(typehint.mask_uses); + self.scope.extend_sys_imports(typehint.mask_uses); ( "ZEND_ARG_INFO_FOR_FUNCTION_WITH_RETURN_OBJ_TYPE_INFO", format!( @@ -960,7 +960,9 @@ impl ArgInfoBuilder<'_> { } } VerificationMode::DebugAssert => { - let extra_flags_in_verification = &flags_in_verification - &self.scope.sys; + let extra_flags_in_verification = self + .scope + .excluding_existing_sys_imports(&flags_in_verification); if let Some(extra_use) = ImportsUsed::as_use_single(&extra_flags_in_verification, "rsphp_sys") { @@ -1422,11 +1424,12 @@ impl ArgInfoBuilder<'_> { } } VerificationMode::DebugAssert => { - let extra_verification_imports = &verification_imports - .iter() - .map(|import| (*import).to_owned()) - .collect::>() - - &self.scope.sys; + let extra_verification_imports = self.scope.excluding_existing_sys_imports( + &verification_imports + .iter() + .map(|import| (*import).to_owned()) + .collect::>(), + ); if let Some(extra_use) = ImportsUsed::as_use_single(&extra_verification_imports, "rsphp_sys") { diff --git a/crates/stub-parser/src/imports.rs b/crates/stub-parser/src/imports.rs index d86175a..d644834 100644 --- a/crates/stub-parser/src/imports.rs +++ b/crates/stub-parser/src/imports.rs @@ -2,10 +2,9 @@ use std::collections::HashSet; /// Tracker for what imports need to be added to a given scope. #[derive(Default)] -#[expect(clippy::field_scoped_visibility_modifiers, reason = "To be refactored")] pub(crate) struct ImportsUsed { /// Imports needed from `rsphp_sys`. - pub(crate) sys: HashSet, + sys: HashSet, /// Imports needed from `rsphp_macros_c`. macros_c: HashSet, /// Imports needed from `rsphp_macros_rs`. @@ -39,6 +38,23 @@ impl ImportsUsed { } } + /// Add a few more imports for `rsphp_sys`. + pub(crate) fn extend_sys_imports(&mut self, names: T) + where + T: IntoIterator, + { + self.sys.extend(names); + } + + /// Identify the imports in the provided `HashSet` that are *not* in + /// the current imports for `rsphp_sys`. + pub(crate) fn excluding_existing_sys_imports( + &self, + target: &HashSet, + ) -> HashSet { + target - &self.sys + } + /// Format all imports. pub(crate) fn as_uses(&self) -> String { vec![ From 0623a3b6fa9ed5a90fa3adaa0c83523852ff8d23 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Mon, 12 Oct 2026 14:55:32 -0700 Subject: [PATCH 08/12] Stub parser: continue `ImportsUsed` refactor --- crates/stub-parser/src/builder.rs | 100 ++++++++++-------------------- crates/stub-parser/src/imports.rs | 8 ++- crates/stub-parser/src/types.rs | 4 -- 3 files changed, 40 insertions(+), 72 deletions(-) diff --git a/crates/stub-parser/src/builder.rs b/crates/stub-parser/src/builder.rs index 210b731..fddca71 100644 --- a/crates/stub-parser/src/builder.rs +++ b/crates/stub-parser/src/builder.rs @@ -64,10 +64,6 @@ impl TryFrom for ClassLikeMember { } } -#[expect( - unused_results, - reason = "HashSet used to prevent duplication, just need to include the import" -)] impl ClassLikeType { /// Name of the class/interface/enum. fn name(&self) -> &str { @@ -170,7 +166,6 @@ impl ClassLikeType { /// the given number of methods, is in the given namespace, and the overall /// program where the class is declared is passed to allow fetching /// unprocessed details. - #[expect(clippy::too_many_lines, reason = "It's a complicated function")] fn init_and_register_code( &self, scope: &mut ImportsUsed, @@ -183,22 +178,16 @@ impl ClassLikeType { Some(ns) => format!("{ns}\\\\"), None => String::new(), }; - let add_interfaces = self.interfaces_applied_code(scope); - let (macro_name, macro_args) = match (namespace, method_count) { - (Some(ns), 0) => ( - "INIT_NS_CLASS_ENTRY", - format!("(ce, \"{ns}\", \"{name}\", NULL)"), - ), - (Some(ns), _) => ( - "INIT_NS_CLASS_ENTRY", - format!("(ce, \"{ns}\", \"{name}\", methods)"), - ), - (None, 0) => ("INIT_CLASS_ENTRY", format!("(ce, \"{name}\", NULL)")), - (None, _) => ("INIT_CLASS_ENTRY", format!("(ce, \"{name}\", methods)")), + let (macro_name, macro_ns_arg) = match namespace { + Some(ns) => ("INIT_NS_CLASS_ENTRY", format!(" \"{ns}\",")), + None => ("INIT_CLASS_ENTRY", String::new()), }; - let init_macro = format!("{macro_name}!{macro_args};"); - - match self { + let init_macro = format!( + "{macro_name}!(ce,{macro_ns_arg} \"{name}\", {methods});", + methods = if method_count == 0 { "NULL" } else { "methods" } + ); + scope.add_import("rsphp_sys", "zend_class_entry"); + let result = match self { ClassLikeType::Class(class) => { let extends_param = self .parent_class_var() @@ -208,17 +197,14 @@ impl ClassLikeType { flags.push("ZEND_ACC_FINAL"); scope.add_import("rsphp_sys", "ZEND_ACC_FINAL"); } - match class.doc_comment.as_ref() { - None => (), - Some(comment) => { - if comment.text.contains("@strict-properties") { - flags.push("ZEND_ACC_NO_DYNAMIC_PROPERTIES"); - scope.add_import("rsphp_sys", "ZEND_ACC_NO_DYNAMIC_PROPERTIES"); - } - if comment.text.contains("@not-serializable") { - flags.push("ZEND_ACC_NOT_SERIALIZABLE"); - scope.add_import("rsphp_sys", "ZEND_ACC_NOT_SERIALIZABLE"); - } + if let Some(comment) = class.doc_comment.as_ref() { + if comment.text.contains("@strict-properties") { + flags.push("ZEND_ACC_NO_DYNAMIC_PROPERTIES"); + scope.add_import("rsphp_sys", "ZEND_ACC_NO_DYNAMIC_PROPERTIES"); + } + if comment.text.contains("@not-serializable") { + flags.push("ZEND_ACC_NOT_SERIALIZABLE"); + scope.add_import("rsphp_sys", "ZEND_ACC_NOT_SERIALIZABLE"); } } let flags_str = if flags.is_empty() { @@ -226,10 +212,9 @@ impl ClassLikeType { } else { flags.join("|") }; - scope.add_import("rsphp_sys", "zend_class_entry"); scope.add_import("rsphp_sys", "zend_register_internal_class_with_flags"); scope.add_import("rsphp_macros_c", macro_name); - Ok(format!( + format!( "\n{space}let mut ce: zend_class_entry = unsafe {{ std::mem::zeroed() }};\ \n{space}{init_macro}\ \n{space}let class_entry: *mut zend_class_entry = unsafe {{\ @@ -238,10 +223,9 @@ impl ClassLikeType { \n{space}{space}{space}{extends_param},\ \n{space}{space}{space}{flags_str},\ \n{space}{space})\ - \n{space}}};\ - {add_interfaces}", + \n{space}}};", space = " ", - )) + ) } ClassLikeType::PhpEnum(php_enum) => { if method_count != 0 { @@ -252,55 +236,43 @@ impl ClassLikeType { let end: usize = span.end.try_into().expect("Valid usize"); program.get(start..end).expect("Valid byte range for span") }; - let backing = php_enum - .scalar_type - .as_ref() - .map(|type_name| match get_raw_code(type_name.span) { - "string" => Ok(BuiltinType::String), - "int" => Ok(BuiltinType::Int), - _ => Err(format!("Unhandled enum backing type for enum {self:#?}")), - }) - .transpose()?; - let backing_type_code = match backing { + let backing_type_code = match php_enum.scalar_type.as_ref() { None => "IS_UNDEF", - Some(BuiltinType::String) => "IS_STRING", - Some(BuiltinType::Int) => "IS_LONG", - Some(backing_type) => { - return Err(format!("Invalid backing type: {backing_type:?}")); - } + Some(type_name) => match get_raw_code(type_name.span) { + "string" => "IS_STRING", + "int" => "IS_LONG", + _ => return Err(format!("Unhandled enum backing type for enum {self:#?}")), + }, }; - scope.add_import("rsphp_sys", "zend_class_entry"); scope.add_import("rsphp_sys", backing_type_code); scope.add_import("rsphp_sys", "zend_register_internal_enum"); - Ok(format!( + format!( "\n{space}let class_entry: *mut zend_class_entry = unsafe {{\ \n{space}{space}zend_register_internal_enum(\ \n{space}{space}{space}c\"{ns_name_segment}{name}\".as_ptr(),\ \n{space}{space}{space}{backing_type_code},\ \n{space}{space}{space}std::ptr::null_mut(),\ \n{space}{space})\ - \n{space}}};\ - {add_interfaces}", + \n{space}}};", space = " ", - )) + ) } ClassLikeType::Interface(_) => { - scope.add_import("rsphp_sys", "zend_class_entry"); scope.add_import("rsphp_sys", "zend_register_internal_interface"); scope.add_import("rsphp_macros_c", macro_name); - Ok(format!( + format!( "\n{space}let mut ce: zend_class_entry = unsafe {{ std::mem::zeroed() }};\ \n{space}{init_macro}\ \n{space}let class_entry: *mut zend_class_entry = unsafe {{\ \n{space}{space}zend_register_internal_interface(\ \n{space}{space}{space}std::ptr::from_mut::(&mut ce),\ \n{space}{space})\ - \n{space}}};\ - {add_interfaces}", + \n{space}}};", space = " ", - )) + ) } - } + }; + Ok(result + &self.interfaces_applied_code(scope)) } /// The kind of class-like-type this is, one of "enum", "interface", or /// "class". @@ -355,10 +327,6 @@ impl<'program> ArgInfoBuilder<'program> { } } -#[expect( - unused_results, - reason = "HashSet used to prevent duplication, just need to include the import" -)] #[expect(clippy::too_many_lines, reason = "Many complicated functions")] impl ArgInfoBuilder<'_> { /// Get a snippet of the raw code in the program at the location specified diff --git a/crates/stub-parser/src/imports.rs b/crates/stub-parser/src/imports.rs index d644834..fc24b03 100644 --- a/crates/stub-parser/src/imports.rs +++ b/crates/stub-parser/src/imports.rs @@ -26,7 +26,11 @@ impl ImportsUsed { clippy::panic, reason = "Indicates a bug in the library, surfaced at build time" )] - pub(crate) fn add_import(&mut self, module: &str, name: T) -> bool + #[expect( + unused_results, + reason = "HashSet used to prevent duplication, just need to include the import" + )] + pub(crate) fn add_import(&mut self, module: &str, name: T) where T: Into, { @@ -35,7 +39,7 @@ impl ImportsUsed { "rsphp_macros_c" => self.macros_c.insert(name.into()), "rsphp_macros_rs" => self.macros_rs.insert(name.into()), _ => panic!("Invalid use for module {module}"), - } + }; } /// Add a few more imports for `rsphp_sys`. diff --git a/crates/stub-parser/src/types.rs b/crates/stub-parser/src/types.rs index 747079c..1aa27f2 100644 --- a/crates/stub-parser/src/types.rs +++ b/crates/stub-parser/src/types.rs @@ -39,10 +39,6 @@ pub(crate) enum ArgInfoTypeRepr { Union(Vec), } -#[expect( - unused_results, - reason = "HashSet used to prevent duplication, just need to include the import" -)] impl ArgInfoTypeRepr { /// Create an `ArgInfoTypeRepr` for the given `TypeHintKind`, resolving /// names in the given namespace. From 460668e2ed25ecc6b9b731647e35351c5943809b Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Mon, 12 Oct 2026 15:09:43 -0700 Subject: [PATCH 09/12] Stub parser: try to avoid single-use imports Calls to register classes/interfaces/enums, and the initialization macros, are the only things are their respective lines, and the names are not repeated within the same scope. Use fully qualified paths instead of adding additional `use`s for those. Same with some of the bits for registration verification. --- crates/stub-parser/src/builder.rs | 13 ++++------ crates/stub-parser/src/verification.rs | 9 +++---- .../cases/attribute_registration.expected | 8 +++---- .../tests/cases/backed_enum.expected | 4 ++-- .../cases/class_constant_unknown.expected | 8 +++---- .../tests/cases/class_constants.expected | 24 +++++++++---------- .../tests/cases/class_extends.expected | 14 +++++------ .../tests/cases/class_flags.expected | 21 +++++++--------- .../tests/cases/class_implements.expected | 14 +++++------ .../tests/cases/class_registration.expected | 8 +++---- .../cases/default_unknown_param.expected | 8 +++---- crates/stub-parser/tests/cases/enum.expected | 4 ++-- .../tests/cases/enum_types.expected | 12 +++++----- .../tests/cases/interface_extends.expected | 7 +++--- .../cases/interface_registration.expected | 12 +++++----- .../tests/cases/method_attribs.expected | 8 +++---- .../stub-parser/tests/cases/methods.expected | 8 +++---- .../tests/cases/namespaced_bodies.expected | 14 +++++------ .../tests/cases/namespaced_default.expected | 8 +++---- .../tests/cases/namespaced_semi.expected | 7 +++--- .../tests/cases/namespaced_types.expected | 12 +++++----- .../tests/cases/nullable_property.expected | 8 +++---- .../tests/cases/optional_object.expected | 22 ++++++++--------- .../tests/cases/property_defaults.expected | 8 +++---- .../tests/cases/tentative_return.expected | 7 +++--- .../tests/cases/typed_properties.expected | 24 +++++++++---------- .../tests/cases/variadic_param.expected | 8 +++---- .../interface_registration.expected | 20 ++++++++-------- .../method_attribs.expected | 16 ++++++------- .../tests/verification-cases/methods.expected | 15 ++++++------ .../namespaced_semi.expected | 15 ++++++------ .../verification-cases/no_methods.expected | 19 +++++++-------- 32 files changed, 180 insertions(+), 205 deletions(-) diff --git a/crates/stub-parser/src/builder.rs b/crates/stub-parser/src/builder.rs index fddca71..09fe291 100644 --- a/crates/stub-parser/src/builder.rs +++ b/crates/stub-parser/src/builder.rs @@ -183,7 +183,7 @@ impl ClassLikeType { None => ("INIT_CLASS_ENTRY", String::new()), }; let init_macro = format!( - "{macro_name}!(ce,{macro_ns_arg} \"{name}\", {methods});", + "rsphp_macros_c::{macro_name}!(ce,{macro_ns_arg} \"{name}\", {methods});", methods = if method_count == 0 { "NULL" } else { "methods" } ); scope.add_import("rsphp_sys", "zend_class_entry"); @@ -212,13 +212,11 @@ impl ClassLikeType { } else { flags.join("|") }; - scope.add_import("rsphp_sys", "zend_register_internal_class_with_flags"); - scope.add_import("rsphp_macros_c", macro_name); format!( "\n{space}let mut ce: zend_class_entry = unsafe {{ std::mem::zeroed() }};\ \n{space}{init_macro}\ \n{space}let class_entry: *mut zend_class_entry = unsafe {{\ - \n{space}{space}zend_register_internal_class_with_flags(\ + \n{space}{space}rsphp_sys::zend_register_internal_class_with_flags(\ \n{space}{space}{space}std::ptr::from_mut::(&mut ce),\ \n{space}{space}{space}{extends_param},\ \n{space}{space}{space}{flags_str},\ @@ -245,10 +243,9 @@ impl ClassLikeType { }, }; scope.add_import("rsphp_sys", backing_type_code); - scope.add_import("rsphp_sys", "zend_register_internal_enum"); format!( "\n{space}let class_entry: *mut zend_class_entry = unsafe {{\ - \n{space}{space}zend_register_internal_enum(\ + \n{space}{space}rsphp_sys::zend_register_internal_enum(\ \n{space}{space}{space}c\"{ns_name_segment}{name}\".as_ptr(),\ \n{space}{space}{space}{backing_type_code},\ \n{space}{space}{space}std::ptr::null_mut(),\ @@ -258,13 +255,11 @@ impl ClassLikeType { ) } ClassLikeType::Interface(_) => { - scope.add_import("rsphp_sys", "zend_register_internal_interface"); - scope.add_import("rsphp_macros_c", macro_name); format!( "\n{space}let mut ce: zend_class_entry = unsafe {{ std::mem::zeroed() }};\ \n{space}{init_macro}\ \n{space}let class_entry: *mut zend_class_entry = unsafe {{\ - \n{space}{space}zend_register_internal_interface(\ + \n{space}{space}rsphp_sys::zend_register_internal_interface(\ \n{space}{space}{space}std::ptr::from_mut::(&mut ce),\ \n{space}{space})\ \n{space}}};", diff --git a/crates/stub-parser/src/verification.rs b/crates/stub-parser/src/verification.rs index c2963a3..bf7a0f1 100644 --- a/crates/stub-parser/src/verification.rs +++ b/crates/stub-parser/src/verification.rs @@ -149,9 +149,6 @@ impl VerificationData { /// Imports needed for infrastructure, at a file level. pub(crate) const VERIFICATION_RSPHP_SYS_IMPORTS: &[&str] = &[ - "_ZEND_TYPE_LIST_BIT", - "_ZEND_TYPE_LITERAL_NAME_BIT", - "_ZEND_TYPE_NAME_BIT", "zend_function_entry", "zend_internal_arg_info", "zend_string", @@ -197,7 +194,7 @@ fn types_match(expected: &zend_type, actual: &zend_type) -> Option { return Some("Expected a pointer to be set in the actual type".to_owned()); } // Compare the values depending on the flags - if expected.type_mask & _ZEND_TYPE_LITERAL_NAME_BIT != 0 { + if expected.type_mask & rsphp_sys::_ZEND_TYPE_LITERAL_NAME_BIT != 0 { // CString pointers let expected_name = unsafe { CStr::from_ptr(expected.ptr.cast::()) }; let actual_name = unsafe { CStr::from_ptr(actual.ptr.cast::()) }; @@ -206,7 +203,7 @@ fn types_match(expected: &zend_type, actual: &zend_type) -> Option { } return Some(format!("Expected literal name {expected_name:?}, found {actual_name:?}")); } - if expected.type_mask & _ZEND_TYPE_NAME_BIT != 0 { + if expected.type_mask & rsphp_sys::_ZEND_TYPE_NAME_BIT != 0 { // zend_string pointers, compare them by comparing the lengths and values let expected_name: &zend_string = unsafe { expected.ptr.cast::().as_ref().expect("Valid pointer") }; let actual_name: &zend_string = unsafe { actual.ptr.cast::().as_ref().expect("Valid pointer") }; @@ -220,7 +217,7 @@ fn types_match(expected: &zend_type, actual: &zend_type) -> Option { } return Some(format!("Expected name {expected_slice:?}, found {actual_slice:?}")); } - if expected.type_mask & _ZEND_TYPE_LIST_BIT != 0 { + if expected.type_mask & rsphp_sys::_ZEND_TYPE_LIST_BIT != 0 { // zend_type_list pointers let expected_list: &zend_type_list = unsafe { expected.ptr.cast::().as_ref().expect("Valid pointer") }; let acutal_list: &zend_type_list = unsafe { actual.ptr.cast::().as_ref().expect("Valid pointer") }; diff --git a/crates/stub-parser/tests/cases/attribute_registration.expected b/crates/stub-parser/tests/cases/attribute_registration.expected index a95a903..fdcd3a1 100644 --- a/crates/stub-parser/tests/cases/attribute_registration.expected +++ b/crates/stub-parser/tests/cases/attribute_registration.expected @@ -1,9 +1,9 @@ /// Module for `CustomCastable` class. pub(crate) mod CustomCastable { - use rsphp_macros_c::{INIT_CLASS_ENTRY, ZVAL_LONG}; + use rsphp_macros_c::ZVAL_LONG; use rsphp_macros_rs::ZEND_ARG_INFO_FOR_FUNCTION; - use rsphp_sys::{ZEND_ATTRIBUTE_TARGET_CLASS, zend_add_class_attribute, zend_attribute, zend_class_entry, zend_function_entry, zend_internal_arg_info, zend_register_internal_class_with_flags, zend_string, zend_string_init_interned, zend_string_release_ex}; + use rsphp_sys::{ZEND_ATTRIBUTE_TARGET_CLASS, zend_add_class_attribute, zend_attribute, zend_class_entry, zend_function_entry, zend_internal_arg_info, zend_string, zend_string_init_interned, zend_string_release_ex}; /// Argument information for `CustomCastable::__construct()`. pub(crate) const ARGINFO___CONSTRUCT: [zend_internal_arg_info; 1] = [ @@ -15,9 +15,9 @@ pub(crate) mod CustomCastable { assert_eq!(2, methods.len(), "CustomCastable should have 1 methods"); let mut ce: zend_class_entry = unsafe { std::mem::zeroed() }; - INIT_CLASS_ENTRY!(ce, "CustomCastable", methods); + rsphp_macros_c::INIT_CLASS_ENTRY!(ce, "CustomCastable", methods); let class_entry: *mut zend_class_entry = unsafe { - zend_register_internal_class_with_flags( + rsphp_sys::zend_register_internal_class_with_flags( std::ptr::from_mut::(&mut ce), std::ptr::null_mut(), 0, diff --git a/crates/stub-parser/tests/cases/backed_enum.expected b/crates/stub-parser/tests/cases/backed_enum.expected index eddbf0c..5c45c8b 100644 --- a/crates/stub-parser/tests/cases/backed_enum.expected +++ b/crates/stub-parser/tests/cases/backed_enum.expected @@ -2,13 +2,13 @@ /// Module for `Demo` enum. pub(crate) mod Demo { use rsphp_macros_c::ZVAL_STR; - use rsphp_sys::{IS_STRING, zend_class_entry, zend_enum_add_case_cstr, zend_register_internal_enum, zend_string, zend_string_init, zval}; + use rsphp_sys::{IS_STRING, zend_class_entry, zend_enum_add_case_cstr, zend_string, zend_string_init, zval}; /// Register the `Demo` enum. pub(crate) fn register_enum() -> *mut zend_class_entry { let class_entry: *mut zend_class_entry = unsafe { - zend_register_internal_enum( + rsphp_sys::zend_register_internal_enum( c"Demo".as_ptr(), IS_STRING, std::ptr::null_mut(), diff --git a/crates/stub-parser/tests/cases/class_constant_unknown.expected b/crates/stub-parser/tests/cases/class_constant_unknown.expected index 4d8260a..42d5b6b 100644 --- a/crates/stub-parser/tests/cases/class_constant_unknown.expected +++ b/crates/stub-parser/tests/cases/class_constant_unknown.expected @@ -1,16 +1,16 @@ /// Module for `Demo` class. pub(crate) mod Demo { - use rsphp_macros_c::{INIT_CLASS_ENTRY, ZEND_TYPE_INIT_MASK, ZVAL_LONG}; - use rsphp_sys::{MAY_BE_LONG, ZEND_ACC_DEPRECATED, ZEND_ACC_PUBLIC, zend_class_entry, zend_declare_typed_class_constant, zend_register_internal_class_with_flags, zend_string, zend_string_init_interned, zend_string_release_ex, zval}; + use rsphp_macros_c::{ZEND_TYPE_INIT_MASK, ZVAL_LONG}; + use rsphp_sys::{MAY_BE_LONG, ZEND_ACC_DEPRECATED, ZEND_ACC_PUBLIC, zend_class_entry, zend_declare_typed_class_constant, zend_string, zend_string_init_interned, zend_string_release_ex, zval}; /// Register the `Demo` class. pub(crate) fn register_class() -> *mut zend_class_entry { let mut ce: zend_class_entry = unsafe { std::mem::zeroed() }; - INIT_CLASS_ENTRY!(ce, "Demo", NULL); + rsphp_macros_c::INIT_CLASS_ENTRY!(ce, "Demo", NULL); let class_entry: *mut zend_class_entry = unsafe { - zend_register_internal_class_with_flags( + rsphp_sys::zend_register_internal_class_with_flags( std::ptr::from_mut::(&mut ce), std::ptr::null_mut(), 0, diff --git a/crates/stub-parser/tests/cases/class_constants.expected b/crates/stub-parser/tests/cases/class_constants.expected index ad029ae..a3f3337 100644 --- a/crates/stub-parser/tests/cases/class_constants.expected +++ b/crates/stub-parser/tests/cases/class_constants.expected @@ -1,9 +1,9 @@ /// Module for `CustomCastable` class. pub(crate) mod CustomCastable { - use rsphp_macros_c::{INIT_CLASS_ENTRY, ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE, ZEND_TYPE_INIT_MASK, ZVAL_LONG, ZVAL_UNDEF}; + use rsphp_macros_c::{ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE, ZEND_TYPE_INIT_MASK, ZVAL_LONG, ZVAL_UNDEF}; use rsphp_macros_rs::ZEND_ARG_INFO_FOR_FUNCTION; - use rsphp_sys::{IS_LONG, MAY_BE_LONG, ZEND_ACC_FINAL, ZEND_ACC_PRIVATE, ZEND_ACC_PUBLIC, zend_class_entry, zend_declare_typed_class_constant, zend_declare_typed_property, zend_function_entry, zend_internal_arg_info, zend_register_internal_class_with_flags, zend_string, zend_string_init, zend_string_init_interned, zend_string_release, zend_string_release_ex, zval}; + use rsphp_sys::{IS_LONG, MAY_BE_LONG, ZEND_ACC_FINAL, ZEND_ACC_PRIVATE, ZEND_ACC_PUBLIC, zend_class_entry, zend_declare_typed_class_constant, zend_declare_typed_property, zend_function_entry, zend_internal_arg_info, zend_string, zend_string_init, zend_string_init_interned, zend_string_release, zend_string_release_ex, zval}; /// Argument information for `CustomCastable::__construct()`. pub(crate) const ARGINFO___CONSTRUCT: [zend_internal_arg_info; 2] = [ @@ -16,9 +16,9 @@ pub(crate) mod CustomCastable { assert_eq!(2, methods.len(), "CustomCastable should have 1 methods"); let mut ce: zend_class_entry = unsafe { std::mem::zeroed() }; - INIT_CLASS_ENTRY!(ce, "CustomCastable", methods); + rsphp_macros_c::INIT_CLASS_ENTRY!(ce, "CustomCastable", methods); let class_entry: *mut zend_class_entry = unsafe { - zend_register_internal_class_with_flags( + rsphp_sys::zend_register_internal_class_with_flags( std::ptr::from_mut::(&mut ce), std::ptr::null_mut(), ZEND_ACC_FINAL, @@ -108,16 +108,16 @@ pub(crate) mod CustomCastable { } /// Module for `ConstantValues` class. pub(crate) mod ConstantValues { - use rsphp_macros_c::{INIT_CLASS_ENTRY, ZEND_TYPE_INIT_MASK, ZVAL_DOUBLE, ZVAL_STR}; - use rsphp_sys::{MAY_BE_DOUBLE, MAY_BE_STRING, PHP_OS, ZEND_ACC_PUBLIC, ZEND_DEBUG, zend_class_entry, zend_declare_typed_class_constant, zend_register_internal_class_with_flags, zend_string, zend_string_init, zend_string_init_interned, zend_string_release_ex, zval}; + use rsphp_macros_c::{ZEND_TYPE_INIT_MASK, ZVAL_DOUBLE, ZVAL_STR}; + use rsphp_sys::{MAY_BE_DOUBLE, MAY_BE_STRING, PHP_OS, ZEND_ACC_PUBLIC, ZEND_DEBUG, zend_class_entry, zend_declare_typed_class_constant, zend_string, zend_string_init, zend_string_init_interned, zend_string_release_ex, zval}; /// Register the `ConstantValues` class. pub(crate) fn register_class() -> *mut zend_class_entry { let mut ce: zend_class_entry = unsafe { std::mem::zeroed() }; - INIT_CLASS_ENTRY!(ce, "ConstantValues", NULL); + rsphp_macros_c::INIT_CLASS_ENTRY!(ce, "ConstantValues", NULL); let class_entry: *mut zend_class_entry = unsafe { - zend_register_internal_class_with_flags( + rsphp_sys::zend_register_internal_class_with_flags( std::ptr::from_mut::(&mut ce), std::ptr::null_mut(), 0, @@ -161,16 +161,16 @@ pub(crate) mod ConstantValues { } /// Module for `ImplicitPublicConstant` class. pub(crate) mod ImplicitPublicConstant { - use rsphp_macros_c::{INIT_CLASS_ENTRY, ZEND_TYPE_INIT_MASK, ZVAL_LONG}; - use rsphp_sys::{MAY_BE_LONG, ZEND_ACC_PUBLIC, zend_class_entry, zend_declare_typed_class_constant, zend_register_internal_class_with_flags, zend_string, zend_string_init_interned, zend_string_release_ex, zval}; + use rsphp_macros_c::{ZEND_TYPE_INIT_MASK, ZVAL_LONG}; + use rsphp_sys::{MAY_BE_LONG, ZEND_ACC_PUBLIC, zend_class_entry, zend_declare_typed_class_constant, zend_string, zend_string_init_interned, zend_string_release_ex, zval}; /// Register the `ImplicitPublicConstant` class. pub(crate) fn register_class() -> *mut zend_class_entry { let mut ce: zend_class_entry = unsafe { std::mem::zeroed() }; - INIT_CLASS_ENTRY!(ce, "ImplicitPublicConstant", NULL); + rsphp_macros_c::INIT_CLASS_ENTRY!(ce, "ImplicitPublicConstant", NULL); let class_entry: *mut zend_class_entry = unsafe { - zend_register_internal_class_with_flags( + rsphp_sys::zend_register_internal_class_with_flags( std::ptr::from_mut::(&mut ce), std::ptr::null_mut(), 0, diff --git a/crates/stub-parser/tests/cases/class_extends.expected b/crates/stub-parser/tests/cases/class_extends.expected index 9ce7715..9c18e02 100644 --- a/crates/stub-parser/tests/cases/class_extends.expected +++ b/crates/stub-parser/tests/cases/class_extends.expected @@ -1,16 +1,15 @@ /// Module for `Base` class. pub(crate) mod Base { - use rsphp_macros_c::INIT_CLASS_ENTRY; - use rsphp_sys::{zend_class_entry, zend_register_internal_class_with_flags}; + use rsphp_sys::zend_class_entry; /// Register the `Base` class. pub(crate) fn register_class() -> *mut zend_class_entry { let mut ce: zend_class_entry = unsafe { std::mem::zeroed() }; - INIT_CLASS_ENTRY!(ce, "Base", NULL); + rsphp_macros_c::INIT_CLASS_ENTRY!(ce, "Base", NULL); let class_entry: *mut zend_class_entry = unsafe { - zend_register_internal_class_with_flags( + rsphp_sys::zend_register_internal_class_with_flags( std::ptr::from_mut::(&mut ce), std::ptr::null_mut(), 0, @@ -23,16 +22,15 @@ pub(crate) mod Base { } /// Module for `Demo` class. pub(crate) mod Demo { - use rsphp_macros_c::INIT_CLASS_ENTRY; - use rsphp_sys::{zend_class_entry, zend_register_internal_class_with_flags}; + use rsphp_sys::zend_class_entry; /// Register the `Demo` class. pub(crate) fn register_class(zend_ce_Base: *mut zend_class_entry) -> *mut zend_class_entry { let mut ce: zend_class_entry = unsafe { std::mem::zeroed() }; - INIT_CLASS_ENTRY!(ce, "Demo", NULL); + rsphp_macros_c::INIT_CLASS_ENTRY!(ce, "Demo", NULL); let class_entry: *mut zend_class_entry = unsafe { - zend_register_internal_class_with_flags( + rsphp_sys::zend_register_internal_class_with_flags( std::ptr::from_mut::(&mut ce), zend_ce_Base, 0, diff --git a/crates/stub-parser/tests/cases/class_flags.expected b/crates/stub-parser/tests/cases/class_flags.expected index 30166aa..9adf97b 100644 --- a/crates/stub-parser/tests/cases/class_flags.expected +++ b/crates/stub-parser/tests/cases/class_flags.expected @@ -1,16 +1,15 @@ /// Module for `Demo` class. pub(crate) mod Demo { - use rsphp_macros_c::INIT_CLASS_ENTRY; - use rsphp_sys::{ZEND_ACC_FINAL, ZEND_ACC_NO_DYNAMIC_PROPERTIES, zend_class_entry, zend_register_internal_class_with_flags}; + use rsphp_sys::{ZEND_ACC_FINAL, ZEND_ACC_NO_DYNAMIC_PROPERTIES, zend_class_entry}; /// Register the `Demo` class. pub(crate) fn register_class() -> *mut zend_class_entry { let mut ce: zend_class_entry = unsafe { std::mem::zeroed() }; - INIT_CLASS_ENTRY!(ce, "Demo", NULL); + rsphp_macros_c::INIT_CLASS_ENTRY!(ce, "Demo", NULL); let class_entry: *mut zend_class_entry = unsafe { - zend_register_internal_class_with_flags( + rsphp_sys::zend_register_internal_class_with_flags( std::ptr::from_mut::(&mut ce), std::ptr::null_mut(), ZEND_ACC_FINAL|ZEND_ACC_NO_DYNAMIC_PROPERTIES, @@ -23,16 +22,15 @@ pub(crate) mod Demo { } /// Module for `Demo2` class. pub(crate) mod Demo2 { - use rsphp_macros_c::INIT_CLASS_ENTRY; - use rsphp_sys::{ZEND_ACC_FINAL, ZEND_ACC_NOT_SERIALIZABLE, zend_class_entry, zend_register_internal_class_with_flags}; + use rsphp_sys::{ZEND_ACC_FINAL, ZEND_ACC_NOT_SERIALIZABLE, zend_class_entry}; /// Register the `Demo2` class. pub(crate) fn register_class() -> *mut zend_class_entry { let mut ce: zend_class_entry = unsafe { std::mem::zeroed() }; - INIT_CLASS_ENTRY!(ce, "Demo2", NULL); + rsphp_macros_c::INIT_CLASS_ENTRY!(ce, "Demo2", NULL); let class_entry: *mut zend_class_entry = unsafe { - zend_register_internal_class_with_flags( + rsphp_sys::zend_register_internal_class_with_flags( std::ptr::from_mut::(&mut ce), std::ptr::null_mut(), ZEND_ACC_FINAL|ZEND_ACC_NOT_SERIALIZABLE, @@ -45,16 +43,15 @@ pub(crate) mod Demo2 { } /// Module for `Demo3` class. pub(crate) mod Demo3 { - use rsphp_macros_c::INIT_CLASS_ENTRY; - use rsphp_sys::{ZEND_ACC_FINAL, ZEND_ACC_NOT_SERIALIZABLE, ZEND_ACC_NO_DYNAMIC_PROPERTIES, zend_class_entry, zend_register_internal_class_with_flags}; + use rsphp_sys::{ZEND_ACC_FINAL, ZEND_ACC_NOT_SERIALIZABLE, ZEND_ACC_NO_DYNAMIC_PROPERTIES, zend_class_entry}; /// Register the `Demo3` class. pub(crate) fn register_class() -> *mut zend_class_entry { let mut ce: zend_class_entry = unsafe { std::mem::zeroed() }; - INIT_CLASS_ENTRY!(ce, "Demo3", NULL); + rsphp_macros_c::INIT_CLASS_ENTRY!(ce, "Demo3", NULL); let class_entry: *mut zend_class_entry = unsafe { - zend_register_internal_class_with_flags( + rsphp_sys::zend_register_internal_class_with_flags( std::ptr::from_mut::(&mut ce), std::ptr::null_mut(), ZEND_ACC_FINAL|ZEND_ACC_NO_DYNAMIC_PROPERTIES|ZEND_ACC_NOT_SERIALIZABLE, diff --git a/crates/stub-parser/tests/cases/class_implements.expected b/crates/stub-parser/tests/cases/class_implements.expected index 2263ebc..5fa486b 100644 --- a/crates/stub-parser/tests/cases/class_implements.expected +++ b/crates/stub-parser/tests/cases/class_implements.expected @@ -1,16 +1,15 @@ /// Module for `IDemo` interface. pub(crate) mod IDemo { - use rsphp_macros_c::INIT_CLASS_ENTRY; - use rsphp_sys::{zend_class_entry, zend_register_internal_interface}; + use rsphp_sys::zend_class_entry; /// Register the `IDemo` interface. pub(crate) fn register_interface() -> *mut zend_class_entry { let mut ce: zend_class_entry = unsafe { std::mem::zeroed() }; - INIT_CLASS_ENTRY!(ce, "IDemo", NULL); + rsphp_macros_c::INIT_CLASS_ENTRY!(ce, "IDemo", NULL); let class_entry: *mut zend_class_entry = unsafe { - zend_register_internal_interface( + rsphp_sys::zend_register_internal_interface( std::ptr::from_mut::(&mut ce), ) }; @@ -21,16 +20,15 @@ pub(crate) mod IDemo { } /// Module for `Demo` class. pub(crate) mod Demo { - use rsphp_macros_c::INIT_CLASS_ENTRY; - use rsphp_sys::{zend_class_entry, zend_class_implements, zend_register_internal_class_with_flags}; + use rsphp_sys::{zend_class_entry, zend_class_implements}; /// Register the `Demo` class. pub(crate) fn register_class(zend_ce_IDemo: *mut zend_class_entry) -> *mut zend_class_entry { let mut ce: zend_class_entry = unsafe { std::mem::zeroed() }; - INIT_CLASS_ENTRY!(ce, "Demo", NULL); + rsphp_macros_c::INIT_CLASS_ENTRY!(ce, "Demo", NULL); let class_entry: *mut zend_class_entry = unsafe { - zend_register_internal_class_with_flags( + rsphp_sys::zend_register_internal_class_with_flags( std::ptr::from_mut::(&mut ce), std::ptr::null_mut(), 0, diff --git a/crates/stub-parser/tests/cases/class_registration.expected b/crates/stub-parser/tests/cases/class_registration.expected index 09709de..b480d32 100644 --- a/crates/stub-parser/tests/cases/class_registration.expected +++ b/crates/stub-parser/tests/cases/class_registration.expected @@ -1,16 +1,16 @@ /// Module for `Demo` class. pub(crate) mod Demo { - use rsphp_macros_c::{INIT_CLASS_ENTRY, ZEND_TYPE_INIT_MASK, ZVAL_UNDEF}; - use rsphp_sys::{MAY_BE_LONG, ZEND_ACC_PRIVATE, zend_class_entry, zend_declare_typed_property, zend_register_internal_class_with_flags, zend_string, zend_string_init, zend_string_release, zval}; + use rsphp_macros_c::{ZEND_TYPE_INIT_MASK, ZVAL_UNDEF}; + use rsphp_sys::{MAY_BE_LONG, ZEND_ACC_PRIVATE, zend_class_entry, zend_declare_typed_property, zend_string, zend_string_init, zend_string_release, zval}; /// Register the `Demo` class. pub(crate) fn register_class() -> *mut zend_class_entry { let mut ce: zend_class_entry = unsafe { std::mem::zeroed() }; - INIT_CLASS_ENTRY!(ce, "Demo", NULL); + rsphp_macros_c::INIT_CLASS_ENTRY!(ce, "Demo", NULL); let class_entry: *mut zend_class_entry = unsafe { - zend_register_internal_class_with_flags( + rsphp_sys::zend_register_internal_class_with_flags( std::ptr::from_mut::(&mut ce), std::ptr::null_mut(), 0, diff --git a/crates/stub-parser/tests/cases/default_unknown_param.expected b/crates/stub-parser/tests/cases/default_unknown_param.expected index 22636cd..ba0f641 100644 --- a/crates/stub-parser/tests/cases/default_unknown_param.expected +++ b/crates/stub-parser/tests/cases/default_unknown_param.expected @@ -1,9 +1,9 @@ /// Module for `Demo` class. pub(crate) mod Demo { - use rsphp_macros_c::{INIT_CLASS_ENTRY, ZEND_ARG_TYPE_INFO}; + use rsphp_macros_c::ZEND_ARG_TYPE_INFO; use rsphp_macros_rs::ZEND_ARG_INFO_FOR_FUNCTION_WITH_RETURN_TYPE_MASK; - use rsphp_sys::{IS_MIXED, MAY_BE_VOID, zend_class_entry, zend_function_entry, zend_internal_arg_info, zend_register_internal_class_with_flags}; + use rsphp_sys::{IS_MIXED, MAY_BE_VOID, zend_class_entry, zend_function_entry, zend_internal_arg_info}; /// Argument information for `Demo::test1()`. pub(crate) const ARGINFO_TEST1: [zend_internal_arg_info; 2] = [ @@ -16,9 +16,9 @@ pub(crate) mod Demo { assert_eq!(2, methods.len(), "Demo should have 1 methods"); let mut ce: zend_class_entry = unsafe { std::mem::zeroed() }; - INIT_CLASS_ENTRY!(ce, "Demo", methods); + rsphp_macros_c::INIT_CLASS_ENTRY!(ce, "Demo", methods); let class_entry: *mut zend_class_entry = unsafe { - zend_register_internal_class_with_flags( + rsphp_sys::zend_register_internal_class_with_flags( std::ptr::from_mut::(&mut ce), std::ptr::null_mut(), 0, diff --git a/crates/stub-parser/tests/cases/enum.expected b/crates/stub-parser/tests/cases/enum.expected index 8936ff0..a39359c 100644 --- a/crates/stub-parser/tests/cases/enum.expected +++ b/crates/stub-parser/tests/cases/enum.expected @@ -1,13 +1,13 @@ /// Module for `CustomCasting\CastableTarget` enum. pub(crate) mod CustomCasting_CastableTarget { - use rsphp_sys::{IS_UNDEF, zend_class_entry, zend_enum_add_case_cstr, zend_register_internal_enum}; + use rsphp_sys::{IS_UNDEF, zend_class_entry, zend_enum_add_case_cstr}; /// Register the `CustomCasting\CastableTarget` enum. pub(crate) fn register_enum() -> *mut zend_class_entry { let class_entry: *mut zend_class_entry = unsafe { - zend_register_internal_enum( + rsphp_sys::zend_register_internal_enum( c"CustomCasting\\CastableTarget".as_ptr(), IS_UNDEF, std::ptr::null_mut(), diff --git a/crates/stub-parser/tests/cases/enum_types.expected b/crates/stub-parser/tests/cases/enum_types.expected index c9dd84a..648b31a 100644 --- a/crates/stub-parser/tests/cases/enum_types.expected +++ b/crates/stub-parser/tests/cases/enum_types.expected @@ -1,13 +1,13 @@ /// Module for `Unbacked` enum. pub(crate) mod Unbacked { - use rsphp_sys::{IS_UNDEF, zend_class_entry, zend_enum_add_case_cstr, zend_register_internal_enum}; + use rsphp_sys::{IS_UNDEF, zend_class_entry, zend_enum_add_case_cstr}; /// Register the `Unbacked` enum. pub(crate) fn register_enum() -> *mut zend_class_entry { let class_entry: *mut zend_class_entry = unsafe { - zend_register_internal_enum( + rsphp_sys::zend_register_internal_enum( c"Unbacked".as_ptr(), IS_UNDEF, std::ptr::null_mut(), @@ -25,13 +25,13 @@ pub(crate) mod Unbacked { /// Module for `IntBacked` enum. pub(crate) mod IntBacked { use rsphp_macros_c::ZVAL_LONG; - use rsphp_sys::{IS_LONG, zend_class_entry, zend_enum_add_case_cstr, zend_register_internal_enum, zval}; + use rsphp_sys::{IS_LONG, zend_class_entry, zend_enum_add_case_cstr, zval}; /// Register the `IntBacked` enum. pub(crate) fn register_enum() -> *mut zend_class_entry { let class_entry: *mut zend_class_entry = unsafe { - zend_register_internal_enum( + rsphp_sys::zend_register_internal_enum( c"IntBacked".as_ptr(), IS_LONG, std::ptr::null_mut(), @@ -57,13 +57,13 @@ pub(crate) mod IntBacked { /// Module for `StringBacked` enum. pub(crate) mod StringBacked { use rsphp_macros_c::ZVAL_STR; - use rsphp_sys::{IS_STRING, zend_class_entry, zend_enum_add_case_cstr, zend_register_internal_enum, zend_string, zend_string_init, zval}; + use rsphp_sys::{IS_STRING, zend_class_entry, zend_enum_add_case_cstr, zend_string, zend_string_init, zval}; /// Register the `StringBacked` enum. pub(crate) fn register_enum() -> *mut zend_class_entry { let class_entry: *mut zend_class_entry = unsafe { - zend_register_internal_enum( + rsphp_sys::zend_register_internal_enum( c"StringBacked".as_ptr(), IS_STRING, std::ptr::null_mut(), diff --git a/crates/stub-parser/tests/cases/interface_extends.expected b/crates/stub-parser/tests/cases/interface_extends.expected index d2a2061..5fe343f 100644 --- a/crates/stub-parser/tests/cases/interface_extends.expected +++ b/crates/stub-parser/tests/cases/interface_extends.expected @@ -1,16 +1,15 @@ /// Module for `HasMagic` interface. pub(crate) mod HasMagic { - use rsphp_macros_c::INIT_CLASS_ENTRY; - use rsphp_sys::{zend_class_entry, zend_class_implements, zend_register_internal_interface}; + use rsphp_sys::{zend_class_entry, zend_class_implements}; /// Register the `HasMagic` interface. pub(crate) fn register_interface(zend_ce_Countable: *mut zend_class_entry, zend_ce_Stringable: *mut zend_class_entry) -> *mut zend_class_entry { let mut ce: zend_class_entry = unsafe { std::mem::zeroed() }; - INIT_CLASS_ENTRY!(ce, "HasMagic", NULL); + rsphp_macros_c::INIT_CLASS_ENTRY!(ce, "HasMagic", NULL); let class_entry: *mut zend_class_entry = unsafe { - zend_register_internal_interface( + rsphp_sys::zend_register_internal_interface( std::ptr::from_mut::(&mut ce), ) }; diff --git a/crates/stub-parser/tests/cases/interface_registration.expected b/crates/stub-parser/tests/cases/interface_registration.expected index f812ec5..65193dd 100644 --- a/crates/stub-parser/tests/cases/interface_registration.expected +++ b/crates/stub-parser/tests/cases/interface_registration.expected @@ -1,13 +1,13 @@ /// Module for `CustomCasting\CastableTarget` enum. pub(crate) mod CustomCasting_CastableTarget { - use rsphp_sys::{IS_UNDEF, zend_class_entry, zend_enum_add_case_cstr, zend_register_internal_enum}; + use rsphp_sys::{IS_UNDEF, zend_class_entry, zend_enum_add_case_cstr}; /// Register the `CustomCasting\CastableTarget` enum. pub(crate) fn register_enum() -> *mut zend_class_entry { let class_entry: *mut zend_class_entry = unsafe { - zend_register_internal_enum( + rsphp_sys::zend_register_internal_enum( c"CustomCasting\\CastableTarget".as_ptr(), IS_UNDEF, std::ptr::null_mut(), @@ -25,9 +25,9 @@ pub(crate) mod CustomCasting_CastableTarget { } /// Module for `CustomCasting\HasCustomCast` interface. pub(crate) mod CustomCasting_HasCustomCast { - use rsphp_macros_c::{INIT_NS_CLASS_ENTRY, ZEND_ARG_OBJ_INFO}; + use rsphp_macros_c::ZEND_ARG_OBJ_INFO; use rsphp_macros_rs::ZEND_ARG_INFO_FOR_FUNCTION; - use rsphp_sys::{zend_class_entry, zend_function_entry, zend_internal_arg_info, zend_register_internal_interface}; + use rsphp_sys::{zend_class_entry, zend_function_entry, zend_internal_arg_info}; /// Argument information for `CustomCasting\HasCustomCast::__doCast()`. pub(crate) const ARGINFO___DOCAST: [zend_internal_arg_info; 2] = [ @@ -40,9 +40,9 @@ pub(crate) mod CustomCasting_HasCustomCast { assert_eq!(2, methods.len(), "CustomCasting\\HasCustomCast should have 1 methods"); let mut ce: zend_class_entry = unsafe { std::mem::zeroed() }; - INIT_NS_CLASS_ENTRY!(ce, "CustomCasting", "HasCustomCast", methods); + rsphp_macros_c::INIT_NS_CLASS_ENTRY!(ce, "CustomCasting", "HasCustomCast", methods); let class_entry: *mut zend_class_entry = unsafe { - zend_register_internal_interface( + rsphp_sys::zend_register_internal_interface( std::ptr::from_mut::(&mut ce), ) }; diff --git a/crates/stub-parser/tests/cases/method_attribs.expected b/crates/stub-parser/tests/cases/method_attribs.expected index e423ced..f3ce178 100644 --- a/crates/stub-parser/tests/cases/method_attribs.expected +++ b/crates/stub-parser/tests/cases/method_attribs.expected @@ -1,9 +1,9 @@ /// Module for `Demo` class. pub(crate) mod Demo { - use rsphp_macros_c::{INIT_CLASS_ENTRY, ZSTR_KNOWN, ZSTR_LEN, ZVAL_STR}; + use rsphp_macros_c::{ZSTR_KNOWN, ZSTR_LEN, ZVAL_STR}; use rsphp_macros_rs::ZEND_ARG_INFO_FOR_FUNCTION_WITH_RETURN_TYPE_MASK; - use rsphp_sys::{HashTable, MAY_BE_VOID, _zend_known_string_id, zend_add_function_attribute, zend_attribute, zend_class_entry, zend_function, zend_function_entry, zend_hash_str_find_ptr, zend_internal_arg_info, zend_register_internal_class_with_flags, zend_string_init, zval}; + use rsphp_sys::{HashTable, MAY_BE_VOID, _zend_known_string_id, zend_add_function_attribute, zend_attribute, zend_class_entry, zend_function, zend_function_entry, zend_hash_str_find_ptr, zend_internal_arg_info, zend_string_init, zval}; /// Argument information for `Demo::test1()`. pub(crate) const ARGINFO_TEST1: [zend_internal_arg_info; 1] = [ @@ -15,9 +15,9 @@ pub(crate) mod Demo { assert_eq!(2, methods.len(), "Demo should have 1 methods"); let mut ce: zend_class_entry = unsafe { std::mem::zeroed() }; - INIT_CLASS_ENTRY!(ce, "Demo", methods); + rsphp_macros_c::INIT_CLASS_ENTRY!(ce, "Demo", methods); let class_entry: *mut zend_class_entry = unsafe { - zend_register_internal_class_with_flags( + rsphp_sys::zend_register_internal_class_with_flags( std::ptr::from_mut::(&mut ce), std::ptr::null_mut(), 0, diff --git a/crates/stub-parser/tests/cases/methods.expected b/crates/stub-parser/tests/cases/methods.expected index ce5b6e2..1361f5f 100644 --- a/crates/stub-parser/tests/cases/methods.expected +++ b/crates/stub-parser/tests/cases/methods.expected @@ -1,9 +1,9 @@ /// Module for `Demo` class. pub(crate) mod Demo { - use rsphp_macros_c::{INIT_CLASS_ENTRY, ZEND_ARG_INFO, ZEND_ARG_INFO_WITH_DEFAULT_VALUE, ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE}; + use rsphp_macros_c::{ZEND_ARG_INFO, ZEND_ARG_INFO_WITH_DEFAULT_VALUE, ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE}; use rsphp_macros_rs::{ZEND_ARG_INFO_FOR_FUNCTION, ZEND_ARG_INFO_FOR_FUNCTION_WITH_RETURN_TYPE_MASK}; - use rsphp_sys::{IS_LONG, IS_STRING, MAY_BE_LONG, MAY_BE_STRING, MAY_BE_VOID, zend_class_entry, zend_function_entry, zend_internal_arg_info, zend_register_internal_class_with_flags}; + use rsphp_sys::{IS_LONG, IS_STRING, MAY_BE_LONG, MAY_BE_STRING, MAY_BE_VOID, zend_class_entry, zend_function_entry, zend_internal_arg_info}; /// Argument information for `Demo::test1()`. pub(crate) const ARGINFO_TEST1: [zend_internal_arg_info; 1] = [ @@ -44,9 +44,9 @@ pub(crate) mod Demo { assert_eq!(7, methods.len(), "Demo should have 6 methods"); let mut ce: zend_class_entry = unsafe { std::mem::zeroed() }; - INIT_CLASS_ENTRY!(ce, "Demo", methods); + rsphp_macros_c::INIT_CLASS_ENTRY!(ce, "Demo", methods); let class_entry: *mut zend_class_entry = unsafe { - zend_register_internal_class_with_flags( + rsphp_sys::zend_register_internal_class_with_flags( std::ptr::from_mut::(&mut ce), std::ptr::null_mut(), 0, diff --git a/crates/stub-parser/tests/cases/namespaced_bodies.expected b/crates/stub-parser/tests/cases/namespaced_bodies.expected index 209e993..654c5cb 100644 --- a/crates/stub-parser/tests/cases/namespaced_bodies.expected +++ b/crates/stub-parser/tests/cases/namespaced_bodies.expected @@ -3,9 +3,8 @@ use rsphp_sys::{MAY_BE_ANY, MAY_BE_VOID, zend_internal_arg_info}; /// Module for `Demo\InDemoNamespace` class. pub(crate) mod Demo_InDemoNamespace { - use rsphp_macros_c::INIT_NS_CLASS_ENTRY; use rsphp_macros_rs::ZEND_ARG_INFO_FOR_FUNCTION; - use rsphp_sys::{zend_class_entry, zend_function_entry, zend_internal_arg_info, zend_register_internal_class_with_flags}; + use rsphp_sys::{zend_class_entry, zend_function_entry, zend_internal_arg_info}; /// Argument information for `Demo\InDemoNamespace::__construct()`. pub(crate) const ARGINFO___CONSTRUCT: [zend_internal_arg_info; 1] = [ @@ -17,9 +16,9 @@ pub(crate) mod Demo_InDemoNamespace { assert_eq!(2, methods.len(), "Demo\\InDemoNamespace should have 1 methods"); let mut ce: zend_class_entry = unsafe { std::mem::zeroed() }; - INIT_NS_CLASS_ENTRY!(ce, "Demo", "InDemoNamespace", methods); + rsphp_macros_c::INIT_NS_CLASS_ENTRY!(ce, "Demo", "InDemoNamespace", methods); let class_entry: *mut zend_class_entry = unsafe { - zend_register_internal_class_with_flags( + rsphp_sys::zend_register_internal_class_with_flags( std::ptr::from_mut::(&mut ce), std::ptr::null_mut(), 0, @@ -37,9 +36,8 @@ pub(crate) const ARGINFO_DEMO_INDEMONAMESPACE: [zend_internal_arg_info; 1] = [ /// Module for `Other\InOtherNamespace` class. pub(crate) mod Other_InOtherNamespace { - use rsphp_macros_c::INIT_NS_CLASS_ENTRY; use rsphp_macros_rs::ZEND_ARG_INFO_FOR_FUNCTION; - use rsphp_sys::{zend_class_entry, zend_function_entry, zend_internal_arg_info, zend_register_internal_class_with_flags}; + use rsphp_sys::{zend_class_entry, zend_function_entry, zend_internal_arg_info}; /// Argument information for `Other\InOtherNamespace::__construct()`. pub(crate) const ARGINFO___CONSTRUCT: [zend_internal_arg_info; 1] = [ @@ -51,9 +49,9 @@ pub(crate) mod Other_InOtherNamespace { assert_eq!(2, methods.len(), "Other\\InOtherNamespace should have 1 methods"); let mut ce: zend_class_entry = unsafe { std::mem::zeroed() }; - INIT_NS_CLASS_ENTRY!(ce, "Other", "InOtherNamespace", methods); + rsphp_macros_c::INIT_NS_CLASS_ENTRY!(ce, "Other", "InOtherNamespace", methods); let class_entry: *mut zend_class_entry = unsafe { - zend_register_internal_class_with_flags( + rsphp_sys::zend_register_internal_class_with_flags( std::ptr::from_mut::(&mut ce), std::ptr::null_mut(), 0, diff --git a/crates/stub-parser/tests/cases/namespaced_default.expected b/crates/stub-parser/tests/cases/namespaced_default.expected index 486ada1..05f8356 100644 --- a/crates/stub-parser/tests/cases/namespaced_default.expected +++ b/crates/stub-parser/tests/cases/namespaced_default.expected @@ -1,9 +1,9 @@ /// Module for `CustomCasting\CustomCastable` class. pub(crate) mod CustomCasting_CustomCastable { - use rsphp_macros_c::{INIT_NS_CLASS_ENTRY, ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE, ZEND_TYPE_INIT_MASK, ZVAL_LONG}; + use rsphp_macros_c::{ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE, ZEND_TYPE_INIT_MASK, ZVAL_LONG}; use rsphp_macros_rs::ZEND_ARG_INFO_FOR_FUNCTION; - use rsphp_sys::{IS_LONG, MAY_BE_LONG, ZEND_ACC_FINAL, ZEND_ACC_PUBLIC, zend_class_entry, zend_declare_typed_class_constant, zend_function_entry, zend_internal_arg_info, zend_register_internal_class_with_flags, zend_string, zend_string_init_interned, zend_string_release_ex, zval}; + use rsphp_sys::{IS_LONG, MAY_BE_LONG, ZEND_ACC_FINAL, ZEND_ACC_PUBLIC, zend_class_entry, zend_declare_typed_class_constant, zend_function_entry, zend_internal_arg_info, zend_string, zend_string_init_interned, zend_string_release_ex, zval}; /// Argument information for `CustomCasting\CustomCastable::__construct()`. pub(crate) const ARGINFO___CONSTRUCT: [zend_internal_arg_info; 2] = [ @@ -16,9 +16,9 @@ pub(crate) mod CustomCasting_CustomCastable { assert_eq!(2, methods.len(), "CustomCasting\\CustomCastable should have 1 methods"); let mut ce: zend_class_entry = unsafe { std::mem::zeroed() }; - INIT_NS_CLASS_ENTRY!(ce, "CustomCasting", "CustomCastable", methods); + rsphp_macros_c::INIT_NS_CLASS_ENTRY!(ce, "CustomCasting", "CustomCastable", methods); let class_entry: *mut zend_class_entry = unsafe { - zend_register_internal_class_with_flags( + rsphp_sys::zend_register_internal_class_with_flags( std::ptr::from_mut::(&mut ce), std::ptr::null_mut(), ZEND_ACC_FINAL, diff --git a/crates/stub-parser/tests/cases/namespaced_semi.expected b/crates/stub-parser/tests/cases/namespaced_semi.expected index 877a15c..f789dbd 100644 --- a/crates/stub-parser/tests/cases/namespaced_semi.expected +++ b/crates/stub-parser/tests/cases/namespaced_semi.expected @@ -3,9 +3,8 @@ use rsphp_sys::{MAY_BE_VOID, zend_internal_arg_info}; /// Module for `Demo\InDemoNamespace` class. pub(crate) mod Demo_InDemoNamespace { - use rsphp_macros_c::INIT_NS_CLASS_ENTRY; use rsphp_macros_rs::ZEND_ARG_INFO_FOR_FUNCTION; - use rsphp_sys::{zend_class_entry, zend_function_entry, zend_internal_arg_info, zend_register_internal_class_with_flags}; + use rsphp_sys::{zend_class_entry, zend_function_entry, zend_internal_arg_info}; /// Argument information for `Demo\InDemoNamespace::__construct()`. pub(crate) const ARGINFO___CONSTRUCT: [zend_internal_arg_info; 1] = [ @@ -17,9 +16,9 @@ pub(crate) mod Demo_InDemoNamespace { assert_eq!(2, methods.len(), "Demo\\InDemoNamespace should have 1 methods"); let mut ce: zend_class_entry = unsafe { std::mem::zeroed() }; - INIT_NS_CLASS_ENTRY!(ce, "Demo", "InDemoNamespace", methods); + rsphp_macros_c::INIT_NS_CLASS_ENTRY!(ce, "Demo", "InDemoNamespace", methods); let class_entry: *mut zend_class_entry = unsafe { - zend_register_internal_class_with_flags( + rsphp_sys::zend_register_internal_class_with_flags( std::ptr::from_mut::(&mut ce), std::ptr::null_mut(), 0, diff --git a/crates/stub-parser/tests/cases/namespaced_types.expected b/crates/stub-parser/tests/cases/namespaced_types.expected index f812ec5..65193dd 100644 --- a/crates/stub-parser/tests/cases/namespaced_types.expected +++ b/crates/stub-parser/tests/cases/namespaced_types.expected @@ -1,13 +1,13 @@ /// Module for `CustomCasting\CastableTarget` enum. pub(crate) mod CustomCasting_CastableTarget { - use rsphp_sys::{IS_UNDEF, zend_class_entry, zend_enum_add_case_cstr, zend_register_internal_enum}; + use rsphp_sys::{IS_UNDEF, zend_class_entry, zend_enum_add_case_cstr}; /// Register the `CustomCasting\CastableTarget` enum. pub(crate) fn register_enum() -> *mut zend_class_entry { let class_entry: *mut zend_class_entry = unsafe { - zend_register_internal_enum( + rsphp_sys::zend_register_internal_enum( c"CustomCasting\\CastableTarget".as_ptr(), IS_UNDEF, std::ptr::null_mut(), @@ -25,9 +25,9 @@ pub(crate) mod CustomCasting_CastableTarget { } /// Module for `CustomCasting\HasCustomCast` interface. pub(crate) mod CustomCasting_HasCustomCast { - use rsphp_macros_c::{INIT_NS_CLASS_ENTRY, ZEND_ARG_OBJ_INFO}; + use rsphp_macros_c::ZEND_ARG_OBJ_INFO; use rsphp_macros_rs::ZEND_ARG_INFO_FOR_FUNCTION; - use rsphp_sys::{zend_class_entry, zend_function_entry, zend_internal_arg_info, zend_register_internal_interface}; + use rsphp_sys::{zend_class_entry, zend_function_entry, zend_internal_arg_info}; /// Argument information for `CustomCasting\HasCustomCast::__doCast()`. pub(crate) const ARGINFO___DOCAST: [zend_internal_arg_info; 2] = [ @@ -40,9 +40,9 @@ pub(crate) mod CustomCasting_HasCustomCast { assert_eq!(2, methods.len(), "CustomCasting\\HasCustomCast should have 1 methods"); let mut ce: zend_class_entry = unsafe { std::mem::zeroed() }; - INIT_NS_CLASS_ENTRY!(ce, "CustomCasting", "HasCustomCast", methods); + rsphp_macros_c::INIT_NS_CLASS_ENTRY!(ce, "CustomCasting", "HasCustomCast", methods); let class_entry: *mut zend_class_entry = unsafe { - zend_register_internal_interface( + rsphp_sys::zend_register_internal_interface( std::ptr::from_mut::(&mut ce), ) }; diff --git a/crates/stub-parser/tests/cases/nullable_property.expected b/crates/stub-parser/tests/cases/nullable_property.expected index 41e8f24..6d00bf1 100644 --- a/crates/stub-parser/tests/cases/nullable_property.expected +++ b/crates/stub-parser/tests/cases/nullable_property.expected @@ -1,16 +1,16 @@ /// Module for `Reference` class. pub(crate) mod Reference { - use rsphp_macros_c::{INIT_CLASS_ENTRY, ZEND_TYPE_INIT_MASK, ZVAL_UNDEF}; - use rsphp_sys::{MAY_BE_NULL, MAY_BE_STRING, ZEND_ACC_FINAL, ZEND_ACC_NOT_SERIALIZABLE, ZEND_ACC_NO_DYNAMIC_PROPERTIES, ZEND_ACC_PUBLIC, zend_class_entry, zend_declare_typed_property, zend_register_internal_class_with_flags, zend_string, zend_string_init, zend_string_release, zval}; + use rsphp_macros_c::{ZEND_TYPE_INIT_MASK, ZVAL_UNDEF}; + use rsphp_sys::{MAY_BE_NULL, MAY_BE_STRING, ZEND_ACC_FINAL, ZEND_ACC_NOT_SERIALIZABLE, ZEND_ACC_NO_DYNAMIC_PROPERTIES, ZEND_ACC_PUBLIC, zend_class_entry, zend_declare_typed_property, zend_string, zend_string_init, zend_string_release, zval}; /// Register the `Reference` class. pub(crate) fn register_class() -> *mut zend_class_entry { let mut ce: zend_class_entry = unsafe { std::mem::zeroed() }; - INIT_CLASS_ENTRY!(ce, "Reference", NULL); + rsphp_macros_c::INIT_CLASS_ENTRY!(ce, "Reference", NULL); let class_entry: *mut zend_class_entry = unsafe { - zend_register_internal_class_with_flags( + rsphp_sys::zend_register_internal_class_with_flags( std::ptr::from_mut::(&mut ce), std::ptr::null_mut(), ZEND_ACC_FINAL|ZEND_ACC_NO_DYNAMIC_PROPERTIES|ZEND_ACC_NOT_SERIALIZABLE, diff --git a/crates/stub-parser/tests/cases/optional_object.expected b/crates/stub-parser/tests/cases/optional_object.expected index d70b142..fd95279 100644 --- a/crates/stub-parser/tests/cases/optional_object.expected +++ b/crates/stub-parser/tests/cases/optional_object.expected @@ -1,9 +1,9 @@ /// Module for `Repository` class. pub(crate) mod Repository { - use rsphp_macros_c::{INIT_CLASS_ENTRY, ZEND_ARG_OBJ_INFO, ZEND_ARG_OBJ_INFO_WITH_DEFAULT_VALUE, ZEND_ARG_TYPE_INFO}; + use rsphp_macros_c::{ZEND_ARG_OBJ_INFO, ZEND_ARG_OBJ_INFO_WITH_DEFAULT_VALUE, ZEND_ARG_TYPE_INFO}; use rsphp_macros_rs::ZEND_ARG_INFO_FOR_FUNCTION_WITH_RETURN_OBJ_INFO; - use rsphp_sys::{IS_ARRAY, IS_STRING, ZEND_ACC_FINAL, ZEND_ACC_NOT_SERIALIZABLE, ZEND_ACC_NO_DYNAMIC_PROPERTIES, zend_class_entry, zend_function_entry, zend_internal_arg_info, zend_register_internal_class_with_flags}; + use rsphp_sys::{IS_ARRAY, IS_STRING, ZEND_ACC_FINAL, ZEND_ACC_NOT_SERIALIZABLE, ZEND_ACC_NO_DYNAMIC_PROPERTIES, zend_class_entry, zend_function_entry, zend_internal_arg_info}; /// Argument information for `Repository::commit()`. pub(crate) const ARGINFO_COMMIT: [zend_internal_arg_info; 6] = [ @@ -20,9 +20,9 @@ pub(crate) mod Repository { assert_eq!(2, methods.len(), "Repository should have 1 methods"); let mut ce: zend_class_entry = unsafe { std::mem::zeroed() }; - INIT_CLASS_ENTRY!(ce, "Repository", methods); + rsphp_macros_c::INIT_CLASS_ENTRY!(ce, "Repository", methods); let class_entry: *mut zend_class_entry = unsafe { - zend_register_internal_class_with_flags( + rsphp_sys::zend_register_internal_class_with_flags( std::ptr::from_mut::(&mut ce), std::ptr::null_mut(), ZEND_ACC_FINAL|ZEND_ACC_NO_DYNAMIC_PROPERTIES|ZEND_ACC_NOT_SERIALIZABLE, @@ -35,16 +35,15 @@ pub(crate) mod Repository { } /// Module for `Oid` class. pub(crate) mod Oid { - use rsphp_macros_c::INIT_CLASS_ENTRY; - use rsphp_sys::{ZEND_ACC_FINAL, ZEND_ACC_NOT_SERIALIZABLE, ZEND_ACC_NO_DYNAMIC_PROPERTIES, zend_class_entry, zend_register_internal_class_with_flags}; + use rsphp_sys::{ZEND_ACC_FINAL, ZEND_ACC_NOT_SERIALIZABLE, ZEND_ACC_NO_DYNAMIC_PROPERTIES, zend_class_entry}; /// Register the `Oid` class. pub(crate) fn register_class() -> *mut zend_class_entry { let mut ce: zend_class_entry = unsafe { std::mem::zeroed() }; - INIT_CLASS_ENTRY!(ce, "Oid", NULL); + rsphp_macros_c::INIT_CLASS_ENTRY!(ce, "Oid", NULL); let class_entry: *mut zend_class_entry = unsafe { - zend_register_internal_class_with_flags( + rsphp_sys::zend_register_internal_class_with_flags( std::ptr::from_mut::(&mut ce), std::ptr::null_mut(), ZEND_ACC_FINAL|ZEND_ACC_NO_DYNAMIC_PROPERTIES|ZEND_ACC_NOT_SERIALIZABLE, @@ -57,16 +56,15 @@ pub(crate) mod Oid { } /// Module for `Signature` class. pub(crate) mod Signature { - use rsphp_macros_c::INIT_CLASS_ENTRY; - use rsphp_sys::{ZEND_ACC_FINAL, ZEND_ACC_NOT_SERIALIZABLE, ZEND_ACC_NO_DYNAMIC_PROPERTIES, zend_class_entry, zend_register_internal_class_with_flags}; + use rsphp_sys::{ZEND_ACC_FINAL, ZEND_ACC_NOT_SERIALIZABLE, ZEND_ACC_NO_DYNAMIC_PROPERTIES, zend_class_entry}; /// Register the `Signature` class. pub(crate) fn register_class() -> *mut zend_class_entry { let mut ce: zend_class_entry = unsafe { std::mem::zeroed() }; - INIT_CLASS_ENTRY!(ce, "Signature", NULL); + rsphp_macros_c::INIT_CLASS_ENTRY!(ce, "Signature", NULL); let class_entry: *mut zend_class_entry = unsafe { - zend_register_internal_class_with_flags( + rsphp_sys::zend_register_internal_class_with_flags( std::ptr::from_mut::(&mut ce), std::ptr::null_mut(), ZEND_ACC_FINAL|ZEND_ACC_NO_DYNAMIC_PROPERTIES|ZEND_ACC_NOT_SERIALIZABLE, diff --git a/crates/stub-parser/tests/cases/property_defaults.expected b/crates/stub-parser/tests/cases/property_defaults.expected index 1bd6184..3da76f9 100644 --- a/crates/stub-parser/tests/cases/property_defaults.expected +++ b/crates/stub-parser/tests/cases/property_defaults.expected @@ -1,16 +1,16 @@ /// Module for `Demo` class. pub(crate) mod Demo { - use rsphp_macros_c::{INIT_CLASS_ENTRY, ZEND_TYPE_INIT_MASK, ZVAL_EMPTY_ARRAY, ZVAL_EMPTY_STRING, ZVAL_FALSE, ZVAL_INTERNED_STR, ZVAL_LONG, ZVAL_NULL, ZVAL_TRUE}; - use rsphp_sys::{MAY_BE_ANY, ZEND_ACC_PUBLIC, zend_class_entry, zend_declare_typed_property, zend_register_internal_class_with_flags, zend_string, zend_string_init, zend_string_release, zval}; + use rsphp_macros_c::{ZEND_TYPE_INIT_MASK, ZVAL_EMPTY_ARRAY, ZVAL_EMPTY_STRING, ZVAL_FALSE, ZVAL_INTERNED_STR, ZVAL_LONG, ZVAL_NULL, ZVAL_TRUE}; + use rsphp_sys::{MAY_BE_ANY, ZEND_ACC_PUBLIC, zend_class_entry, zend_declare_typed_property, zend_string, zend_string_init, zend_string_release, zval}; /// Register the `Demo` class. pub(crate) fn register_class() -> *mut zend_class_entry { let mut ce: zend_class_entry = unsafe { std::mem::zeroed() }; - INIT_CLASS_ENTRY!(ce, "Demo", NULL); + rsphp_macros_c::INIT_CLASS_ENTRY!(ce, "Demo", NULL); let class_entry: *mut zend_class_entry = unsafe { - zend_register_internal_class_with_flags( + rsphp_sys::zend_register_internal_class_with_flags( std::ptr::from_mut::(&mut ce), std::ptr::null_mut(), 0, diff --git a/crates/stub-parser/tests/cases/tentative_return.expected b/crates/stub-parser/tests/cases/tentative_return.expected index c7104b3..e8d3efc 100644 --- a/crates/stub-parser/tests/cases/tentative_return.expected +++ b/crates/stub-parser/tests/cases/tentative_return.expected @@ -1,9 +1,8 @@ /// Module for `Demo` class. pub(crate) mod Demo { - use rsphp_macros_c::INIT_CLASS_ENTRY; use rsphp_macros_rs::{ZEND_ARG_INFO_FOR_FUNCTION_WITH_RETURN_OBJ_INFO, ZEND_ARG_INFO_FOR_FUNCTION_WITH_RETURN_TYPE_MASK}; - use rsphp_sys::{MAY_BE_ARRAY, MAY_BE_LONG, MAY_BE_STRING, zend_class_entry, zend_function_entry, zend_internal_arg_info, zend_register_internal_class_with_flags}; + use rsphp_sys::{MAY_BE_ARRAY, MAY_BE_LONG, MAY_BE_STRING, zend_class_entry, zend_function_entry, zend_internal_arg_info}; /// Argument information for `Demo::test1()`. pub(crate) const ARGINFO_TEST1: [zend_internal_arg_info; 1] = [ @@ -25,9 +24,9 @@ pub(crate) mod Demo { assert_eq!(4, methods.len(), "Demo should have 3 methods"); let mut ce: zend_class_entry = unsafe { std::mem::zeroed() }; - INIT_CLASS_ENTRY!(ce, "Demo", methods); + rsphp_macros_c::INIT_CLASS_ENTRY!(ce, "Demo", methods); let class_entry: *mut zend_class_entry = unsafe { - zend_register_internal_class_with_flags( + rsphp_sys::zend_register_internal_class_with_flags( std::ptr::from_mut::(&mut ce), std::ptr::null_mut(), 0, diff --git a/crates/stub-parser/tests/cases/typed_properties.expected b/crates/stub-parser/tests/cases/typed_properties.expected index 075309a..074968c 100644 --- a/crates/stub-parser/tests/cases/typed_properties.expected +++ b/crates/stub-parser/tests/cases/typed_properties.expected @@ -1,16 +1,16 @@ /// Module for `RSGit\Commit` class. pub(crate) mod RSGit_Commit { - use rsphp_macros_c::{INIT_NS_CLASS_ENTRY, ZEND_TYPE_INIT_CLASS, ZVAL_UNDEF}; - use rsphp_sys::{ZEND_ACC_FINAL, ZEND_ACC_NOT_SERIALIZABLE, ZEND_ACC_NO_DYNAMIC_PROPERTIES, ZEND_ACC_PUBLIC, zend_class_entry, zend_declare_typed_property, zend_register_internal_class_with_flags, zend_string, zend_string_init, zend_string_release, zval}; + use rsphp_macros_c::{ZEND_TYPE_INIT_CLASS, ZVAL_UNDEF}; + use rsphp_sys::{ZEND_ACC_FINAL, ZEND_ACC_NOT_SERIALIZABLE, ZEND_ACC_NO_DYNAMIC_PROPERTIES, ZEND_ACC_PUBLIC, zend_class_entry, zend_declare_typed_property, zend_string, zend_string_init, zend_string_release, zval}; /// Register the `RSGit\Commit` class. pub(crate) fn register_class() -> *mut zend_class_entry { let mut ce: zend_class_entry = unsafe { std::mem::zeroed() }; - INIT_NS_CLASS_ENTRY!(ce, "RSGit", "Commit", NULL); + rsphp_macros_c::INIT_NS_CLASS_ENTRY!(ce, "RSGit", "Commit", NULL); let class_entry: *mut zend_class_entry = unsafe { - zend_register_internal_class_with_flags( + rsphp_sys::zend_register_internal_class_with_flags( std::ptr::from_mut::(&mut ce), std::ptr::null_mut(), ZEND_ACC_FINAL|ZEND_ACC_NO_DYNAMIC_PROPERTIES|ZEND_ACC_NOT_SERIALIZABLE, @@ -112,9 +112,9 @@ pub(crate) mod RSGit_Commit { } /// Module for `RSGit\Oid` class. pub(crate) mod RSGit_Oid { - use rsphp_macros_c::{INIT_NS_CLASS_ENTRY, ZEND_TYPE_INIT_MASK, ZVAL_UNDEF}; + use rsphp_macros_c::{ZEND_TYPE_INIT_MASK, ZVAL_UNDEF}; use rsphp_macros_rs::ZEND_ARG_INFO_FOR_FUNCTION_WITH_RETURN_TYPE_MASK; - use rsphp_sys::{MAY_BE_STRING, ZEND_ACC_FINAL, ZEND_ACC_NOT_SERIALIZABLE, ZEND_ACC_NO_DYNAMIC_PROPERTIES, ZEND_ACC_PUBLIC, zend_class_entry, zend_declare_typed_property, zend_function_entry, zend_internal_arg_info, zend_register_internal_class_with_flags, zend_string, zend_string_init, zend_string_release, zval}; + use rsphp_sys::{MAY_BE_STRING, ZEND_ACC_FINAL, ZEND_ACC_NOT_SERIALIZABLE, ZEND_ACC_NO_DYNAMIC_PROPERTIES, ZEND_ACC_PUBLIC, zend_class_entry, zend_declare_typed_property, zend_function_entry, zend_internal_arg_info, zend_string, zend_string_init, zend_string_release, zval}; /// Argument information for `RSGit\Oid::__toString()`. pub(crate) const ARGINFO___TOSTRING: [zend_internal_arg_info; 1] = [ @@ -126,9 +126,9 @@ pub(crate) mod RSGit_Oid { assert_eq!(2, methods.len(), "RSGit\\Oid should have 1 methods"); let mut ce: zend_class_entry = unsafe { std::mem::zeroed() }; - INIT_NS_CLASS_ENTRY!(ce, "RSGit", "Oid", methods); + rsphp_macros_c::INIT_NS_CLASS_ENTRY!(ce, "RSGit", "Oid", methods); let class_entry: *mut zend_class_entry = unsafe { - zend_register_internal_class_with_flags( + rsphp_sys::zend_register_internal_class_with_flags( std::ptr::from_mut::(&mut ce), std::ptr::null_mut(), ZEND_ACC_FINAL|ZEND_ACC_NO_DYNAMIC_PROPERTIES|ZEND_ACC_NOT_SERIALIZABLE, @@ -163,16 +163,16 @@ pub(crate) mod RSGit_Oid { } /// Module for `RSGit\Signature` class. pub(crate) mod RSGit_Signature { - use rsphp_macros_c::{INIT_NS_CLASS_ENTRY, ZEND_TYPE_INIT_CLASS, ZEND_TYPE_INIT_MASK, ZVAL_UNDEF}; - use rsphp_sys::{MAY_BE_STRING, ZEND_ACC_FINAL, ZEND_ACC_NOT_SERIALIZABLE, ZEND_ACC_NO_DYNAMIC_PROPERTIES, ZEND_ACC_PUBLIC, zend_class_entry, zend_declare_typed_property, zend_register_internal_class_with_flags, zend_string, zend_string_init, zend_string_release, zval}; + use rsphp_macros_c::{ZEND_TYPE_INIT_CLASS, ZEND_TYPE_INIT_MASK, ZVAL_UNDEF}; + use rsphp_sys::{MAY_BE_STRING, ZEND_ACC_FINAL, ZEND_ACC_NOT_SERIALIZABLE, ZEND_ACC_NO_DYNAMIC_PROPERTIES, ZEND_ACC_PUBLIC, zend_class_entry, zend_declare_typed_property, zend_string, zend_string_init, zend_string_release, zval}; /// Register the `RSGit\Signature` class. pub(crate) fn register_class() -> *mut zend_class_entry { let mut ce: zend_class_entry = unsafe { std::mem::zeroed() }; - INIT_NS_CLASS_ENTRY!(ce, "RSGit", "Signature", NULL); + rsphp_macros_c::INIT_NS_CLASS_ENTRY!(ce, "RSGit", "Signature", NULL); let class_entry: *mut zend_class_entry = unsafe { - zend_register_internal_class_with_flags( + rsphp_sys::zend_register_internal_class_with_flags( std::ptr::from_mut::(&mut ce), std::ptr::null_mut(), ZEND_ACC_FINAL|ZEND_ACC_NO_DYNAMIC_PROPERTIES|ZEND_ACC_NOT_SERIALIZABLE, diff --git a/crates/stub-parser/tests/cases/variadic_param.expected b/crates/stub-parser/tests/cases/variadic_param.expected index 5c9def7..26d00bb 100644 --- a/crates/stub-parser/tests/cases/variadic_param.expected +++ b/crates/stub-parser/tests/cases/variadic_param.expected @@ -1,9 +1,9 @@ /// Module for `Demo` class. pub(crate) mod Demo { - use rsphp_macros_c::{INIT_CLASS_ENTRY, ZEND_ARG_VARIADIC_TYPE_INFO}; + use rsphp_macros_c::ZEND_ARG_VARIADIC_TYPE_INFO; use rsphp_macros_rs::ZEND_ARG_INFO_FOR_FUNCTION_WITH_RETURN_TYPE_MASK; - use rsphp_sys::{IS_MIXED, MAY_BE_VOID, zend_class_entry, zend_function_entry, zend_internal_arg_info, zend_register_internal_class_with_flags}; + use rsphp_sys::{IS_MIXED, MAY_BE_VOID, zend_class_entry, zend_function_entry, zend_internal_arg_info}; /// Argument information for `Demo::test1()`. pub(crate) const ARGINFO_TEST1: [zend_internal_arg_info; 2] = [ @@ -16,9 +16,9 @@ pub(crate) mod Demo { assert_eq!(2, methods.len(), "Demo should have 1 methods"); let mut ce: zend_class_entry = unsafe { std::mem::zeroed() }; - INIT_CLASS_ENTRY!(ce, "Demo", methods); + rsphp_macros_c::INIT_CLASS_ENTRY!(ce, "Demo", methods); let class_entry: *mut zend_class_entry = unsafe { - zend_register_internal_class_with_flags( + rsphp_sys::zend_register_internal_class_with_flags( std::ptr::from_mut::(&mut ce), std::ptr::null_mut(), 0, diff --git a/crates/stub-parser/tests/verification-cases/interface_registration.expected b/crates/stub-parser/tests/verification-cases/interface_registration.expected index baf4fcd..2d2321c 100644 --- a/crates/stub-parser/tests/verification-cases/interface_registration.expected +++ b/crates/stub-parser/tests/verification-cases/interface_registration.expected @@ -1,5 +1,5 @@ #[cfg(debug_assertions)] -use rsphp_sys::{_ZEND_TYPE_LIST_BIT, _ZEND_TYPE_LITERAL_NAME_BIT, _ZEND_TYPE_NAME_BIT, zend_function_entry, zend_internal_arg_info, zend_string, zend_type, zend_type_list}; +use rsphp_sys::{zend_function_entry, zend_internal_arg_info, zend_string, zend_type, zend_type_list}; #[cfg(debug_assertions)] use std::ffi::{c_char, CStr}; @@ -39,7 +39,7 @@ fn types_match(expected: &zend_type, actual: &zend_type) -> Option { return Some("Expected a pointer to be set in the actual type".to_owned()); } // Compare the values depending on the flags - if expected.type_mask & _ZEND_TYPE_LITERAL_NAME_BIT != 0 { + if expected.type_mask & rsphp_sys::_ZEND_TYPE_LITERAL_NAME_BIT != 0 { // CString pointers let expected_name = unsafe { CStr::from_ptr(expected.ptr.cast::()) }; let actual_name = unsafe { CStr::from_ptr(actual.ptr.cast::()) }; @@ -48,7 +48,7 @@ fn types_match(expected: &zend_type, actual: &zend_type) -> Option { } return Some(format!("Expected literal name {expected_name:?}, found {actual_name:?}")); } - if expected.type_mask & _ZEND_TYPE_NAME_BIT != 0 { + if expected.type_mask & rsphp_sys::_ZEND_TYPE_NAME_BIT != 0 { // zend_string pointers, compare them by comparing the lengths and values let expected_name: &zend_string = unsafe { expected.ptr.cast::().as_ref().expect("Valid pointer") }; let actual_name: &zend_string = unsafe { actual.ptr.cast::().as_ref().expect("Valid pointer") }; @@ -62,7 +62,7 @@ fn types_match(expected: &zend_type, actual: &zend_type) -> Option { } return Some(format!("Expected name {expected_slice:?}, found {actual_slice:?}")); } - if expected.type_mask & _ZEND_TYPE_LIST_BIT != 0 { + if expected.type_mask & rsphp_sys::_ZEND_TYPE_LIST_BIT != 0 { // zend_type_list pointers let expected_list: &zend_type_list = unsafe { expected.ptr.cast::().as_ref().expect("Valid pointer") }; let acutal_list: &zend_type_list = unsafe { actual.ptr.cast::().as_ref().expect("Valid pointer") }; @@ -209,13 +209,13 @@ fn verify_registration( } /// Module for `CustomCasting\CastableTarget` enum. pub(crate) mod CustomCasting_CastableTarget { - use rsphp_sys::{IS_UNDEF, zend_class_entry, zend_enum_add_case_cstr, zend_register_internal_enum}; + use rsphp_sys::{IS_UNDEF, zend_class_entry, zend_enum_add_case_cstr}; /// Register the `CustomCasting\CastableTarget` enum. pub(crate) fn register_enum() -> *mut zend_class_entry { let class_entry: *mut zend_class_entry = unsafe { - zend_register_internal_enum( + rsphp_sys::zend_register_internal_enum( c"CustomCasting\\CastableTarget".as_ptr(), IS_UNDEF, std::ptr::null_mut(), @@ -233,9 +233,9 @@ pub(crate) mod CustomCasting_CastableTarget { } /// Module for `CustomCasting\HasCustomCast` interface. pub(crate) mod CustomCasting_HasCustomCast { - use rsphp_macros_c::{INIT_NS_CLASS_ENTRY, ZEND_ARG_OBJ_INFO}; + use rsphp_macros_c::ZEND_ARG_OBJ_INFO; use rsphp_macros_rs::ZEND_ARG_INFO_FOR_FUNCTION; - use rsphp_sys::{zend_class_entry, zend_function_entry, zend_internal_arg_info, zend_register_internal_interface}; + use rsphp_sys::{zend_class_entry, zend_function_entry, zend_internal_arg_info}; #[cfg(debug_assertions)] use rsphp_sys::{ZEND_ACC_ABSTRACT, ZEND_ACC_PUBLIC}; #[cfg(debug_assertions)] @@ -261,9 +261,9 @@ pub(crate) mod CustomCasting_HasCustomCast { assert_eq!(2, methods.len(), "CustomCasting\\HasCustomCast should have 1 methods"); let mut ce: zend_class_entry = unsafe { std::mem::zeroed() }; - INIT_NS_CLASS_ENTRY!(ce, "CustomCasting", "HasCustomCast", methods); + rsphp_macros_c::INIT_NS_CLASS_ENTRY!(ce, "CustomCasting", "HasCustomCast", methods); let class_entry: *mut zend_class_entry = unsafe { - zend_register_internal_interface( + rsphp_sys::zend_register_internal_interface( std::ptr::from_mut::(&mut ce), ) }; diff --git a/crates/stub-parser/tests/verification-cases/method_attribs.expected b/crates/stub-parser/tests/verification-cases/method_attribs.expected index 3f8df10..770ff65 100644 --- a/crates/stub-parser/tests/verification-cases/method_attribs.expected +++ b/crates/stub-parser/tests/verification-cases/method_attribs.expected @@ -1,5 +1,5 @@ #[cfg(debug_assertions)] -use rsphp_sys::{_ZEND_TYPE_LIST_BIT, _ZEND_TYPE_LITERAL_NAME_BIT, _ZEND_TYPE_NAME_BIT, zend_function_entry, zend_internal_arg_info, zend_string, zend_type, zend_type_list}; +use rsphp_sys::{zend_function_entry, zend_internal_arg_info, zend_string, zend_type, zend_type_list}; #[cfg(debug_assertions)] use std::ffi::{c_char, CStr}; @@ -39,7 +39,7 @@ fn types_match(expected: &zend_type, actual: &zend_type) -> Option { return Some("Expected a pointer to be set in the actual type".to_owned()); } // Compare the values depending on the flags - if expected.type_mask & _ZEND_TYPE_LITERAL_NAME_BIT != 0 { + if expected.type_mask & rsphp_sys::_ZEND_TYPE_LITERAL_NAME_BIT != 0 { // CString pointers let expected_name = unsafe { CStr::from_ptr(expected.ptr.cast::()) }; let actual_name = unsafe { CStr::from_ptr(actual.ptr.cast::()) }; @@ -48,7 +48,7 @@ fn types_match(expected: &zend_type, actual: &zend_type) -> Option { } return Some(format!("Expected literal name {expected_name:?}, found {actual_name:?}")); } - if expected.type_mask & _ZEND_TYPE_NAME_BIT != 0 { + if expected.type_mask & rsphp_sys::_ZEND_TYPE_NAME_BIT != 0 { // zend_string pointers, compare them by comparing the lengths and values let expected_name: &zend_string = unsafe { expected.ptr.cast::().as_ref().expect("Valid pointer") }; let actual_name: &zend_string = unsafe { actual.ptr.cast::().as_ref().expect("Valid pointer") }; @@ -62,7 +62,7 @@ fn types_match(expected: &zend_type, actual: &zend_type) -> Option { } return Some(format!("Expected name {expected_slice:?}, found {actual_slice:?}")); } - if expected.type_mask & _ZEND_TYPE_LIST_BIT != 0 { + if expected.type_mask & rsphp_sys::_ZEND_TYPE_LIST_BIT != 0 { // zend_type_list pointers let expected_list: &zend_type_list = unsafe { expected.ptr.cast::().as_ref().expect("Valid pointer") }; let acutal_list: &zend_type_list = unsafe { actual.ptr.cast::().as_ref().expect("Valid pointer") }; @@ -209,9 +209,9 @@ fn verify_registration( } /// Module for `Demo` class. pub(crate) mod Demo { - use rsphp_macros_c::{INIT_CLASS_ENTRY, ZSTR_KNOWN, ZSTR_LEN, ZVAL_STR}; + use rsphp_macros_c::{ZSTR_KNOWN, ZSTR_LEN, ZVAL_STR}; use rsphp_macros_rs::ZEND_ARG_INFO_FOR_FUNCTION_WITH_RETURN_TYPE_MASK; - use rsphp_sys::{HashTable, MAY_BE_VOID, _zend_known_string_id, zend_add_function_attribute, zend_attribute, zend_class_entry, zend_function, zend_function_entry, zend_hash_str_find_ptr, zend_internal_arg_info, zend_register_internal_class_with_flags, zend_string_init, zval}; + use rsphp_sys::{HashTable, MAY_BE_VOID, _zend_known_string_id, zend_add_function_attribute, zend_attribute, zend_class_entry, zend_function, zend_function_entry, zend_hash_str_find_ptr, zend_internal_arg_info, zend_string_init, zval}; #[cfg(debug_assertions)] use rsphp_sys::{ZEND_ACC_DEPRECATED, ZEND_ACC_PUBLIC}; #[cfg(debug_assertions)] @@ -236,9 +236,9 @@ pub(crate) mod Demo { assert_eq!(2, methods.len(), "Demo should have 1 methods"); let mut ce: zend_class_entry = unsafe { std::mem::zeroed() }; - INIT_CLASS_ENTRY!(ce, "Demo", methods); + rsphp_macros_c::INIT_CLASS_ENTRY!(ce, "Demo", methods); let class_entry: *mut zend_class_entry = unsafe { - zend_register_internal_class_with_flags( + rsphp_sys::zend_register_internal_class_with_flags( std::ptr::from_mut::(&mut ce), std::ptr::null_mut(), 0, diff --git a/crates/stub-parser/tests/verification-cases/methods.expected b/crates/stub-parser/tests/verification-cases/methods.expected index a6a7acf..c2130ff 100644 --- a/crates/stub-parser/tests/verification-cases/methods.expected +++ b/crates/stub-parser/tests/verification-cases/methods.expected @@ -1,5 +1,5 @@ #[cfg(debug_assertions)] -use rsphp_sys::{_ZEND_TYPE_LIST_BIT, _ZEND_TYPE_LITERAL_NAME_BIT, _ZEND_TYPE_NAME_BIT, zend_function_entry, zend_internal_arg_info, zend_string, zend_type, zend_type_list}; +use rsphp_sys::{zend_function_entry, zend_internal_arg_info, zend_string, zend_type, zend_type_list}; #[cfg(debug_assertions)] use std::ffi::{c_char, CStr}; @@ -39,7 +39,7 @@ fn types_match(expected: &zend_type, actual: &zend_type) -> Option { return Some("Expected a pointer to be set in the actual type".to_owned()); } // Compare the values depending on the flags - if expected.type_mask & _ZEND_TYPE_LITERAL_NAME_BIT != 0 { + if expected.type_mask & rsphp_sys::_ZEND_TYPE_LITERAL_NAME_BIT != 0 { // CString pointers let expected_name = unsafe { CStr::from_ptr(expected.ptr.cast::()) }; let actual_name = unsafe { CStr::from_ptr(actual.ptr.cast::()) }; @@ -48,7 +48,7 @@ fn types_match(expected: &zend_type, actual: &zend_type) -> Option { } return Some(format!("Expected literal name {expected_name:?}, found {actual_name:?}")); } - if expected.type_mask & _ZEND_TYPE_NAME_BIT != 0 { + if expected.type_mask & rsphp_sys::_ZEND_TYPE_NAME_BIT != 0 { // zend_string pointers, compare them by comparing the lengths and values let expected_name: &zend_string = unsafe { expected.ptr.cast::().as_ref().expect("Valid pointer") }; let actual_name: &zend_string = unsafe { actual.ptr.cast::().as_ref().expect("Valid pointer") }; @@ -62,7 +62,7 @@ fn types_match(expected: &zend_type, actual: &zend_type) -> Option { } return Some(format!("Expected name {expected_slice:?}, found {actual_slice:?}")); } - if expected.type_mask & _ZEND_TYPE_LIST_BIT != 0 { + if expected.type_mask & rsphp_sys::_ZEND_TYPE_LIST_BIT != 0 { // zend_type_list pointers let expected_list: &zend_type_list = unsafe { expected.ptr.cast::().as_ref().expect("Valid pointer") }; let acutal_list: &zend_type_list = unsafe { actual.ptr.cast::().as_ref().expect("Valid pointer") }; @@ -209,9 +209,8 @@ fn verify_registration( } /// Module for `Demo` class. pub(crate) mod Demo { - use rsphp_macros_c::INIT_CLASS_ENTRY; use rsphp_macros_rs::ZEND_ARG_INFO_FOR_FUNCTION_WITH_RETURN_TYPE_MASK; - use rsphp_sys::{MAY_BE_VOID, zend_class_entry, zend_function_entry, zend_internal_arg_info, zend_register_internal_class_with_flags}; + use rsphp_sys::{MAY_BE_VOID, zend_class_entry, zend_function_entry, zend_internal_arg_info}; #[cfg(debug_assertions)] use rsphp_sys::{ZEND_ACC_PRIVATE, ZEND_ACC_PROTECTED, ZEND_ACC_PUBLIC, ZEND_ACC_STATIC}; #[cfg(debug_assertions)] @@ -266,9 +265,9 @@ pub(crate) mod Demo { assert_eq!(7, methods.len(), "Demo should have 6 methods"); let mut ce: zend_class_entry = unsafe { std::mem::zeroed() }; - INIT_CLASS_ENTRY!(ce, "Demo", methods); + rsphp_macros_c::INIT_CLASS_ENTRY!(ce, "Demo", methods); let class_entry: *mut zend_class_entry = unsafe { - zend_register_internal_class_with_flags( + rsphp_sys::zend_register_internal_class_with_flags( std::ptr::from_mut::(&mut ce), std::ptr::null_mut(), 0, diff --git a/crates/stub-parser/tests/verification-cases/namespaced_semi.expected b/crates/stub-parser/tests/verification-cases/namespaced_semi.expected index 48bdf72..7eeb67a 100644 --- a/crates/stub-parser/tests/verification-cases/namespaced_semi.expected +++ b/crates/stub-parser/tests/verification-cases/namespaced_semi.expected @@ -1,7 +1,7 @@ use rsphp_macros_rs::ZEND_ARG_INFO_FOR_FUNCTION_WITH_RETURN_TYPE_MASK; use rsphp_sys::{MAY_BE_VOID, zend_internal_arg_info}; #[cfg(debug_assertions)] -use rsphp_sys::{_ZEND_TYPE_LIST_BIT, _ZEND_TYPE_LITERAL_NAME_BIT, _ZEND_TYPE_NAME_BIT, zend_function_entry, zend_string, zend_type, zend_type_list}; +use rsphp_sys::{zend_function_entry, zend_string, zend_type, zend_type_list}; #[cfg(debug_assertions)] use std::ffi::{c_char, CStr}; @@ -41,7 +41,7 @@ fn types_match(expected: &zend_type, actual: &zend_type) -> Option { return Some("Expected a pointer to be set in the actual type".to_owned()); } // Compare the values depending on the flags - if expected.type_mask & _ZEND_TYPE_LITERAL_NAME_BIT != 0 { + if expected.type_mask & rsphp_sys::_ZEND_TYPE_LITERAL_NAME_BIT != 0 { // CString pointers let expected_name = unsafe { CStr::from_ptr(expected.ptr.cast::()) }; let actual_name = unsafe { CStr::from_ptr(actual.ptr.cast::()) }; @@ -50,7 +50,7 @@ fn types_match(expected: &zend_type, actual: &zend_type) -> Option { } return Some(format!("Expected literal name {expected_name:?}, found {actual_name:?}")); } - if expected.type_mask & _ZEND_TYPE_NAME_BIT != 0 { + if expected.type_mask & rsphp_sys::_ZEND_TYPE_NAME_BIT != 0 { // zend_string pointers, compare them by comparing the lengths and values let expected_name: &zend_string = unsafe { expected.ptr.cast::().as_ref().expect("Valid pointer") }; let actual_name: &zend_string = unsafe { actual.ptr.cast::().as_ref().expect("Valid pointer") }; @@ -64,7 +64,7 @@ fn types_match(expected: &zend_type, actual: &zend_type) -> Option { } return Some(format!("Expected name {expected_slice:?}, found {actual_slice:?}")); } - if expected.type_mask & _ZEND_TYPE_LIST_BIT != 0 { + if expected.type_mask & rsphp_sys::_ZEND_TYPE_LIST_BIT != 0 { // zend_type_list pointers let expected_list: &zend_type_list = unsafe { expected.ptr.cast::().as_ref().expect("Valid pointer") }; let acutal_list: &zend_type_list = unsafe { actual.ptr.cast::().as_ref().expect("Valid pointer") }; @@ -211,9 +211,8 @@ fn verify_registration( } /// Module for `Demo\InDemoNamespace` class. pub(crate) mod Demo_InDemoNamespace { - use rsphp_macros_c::INIT_NS_CLASS_ENTRY; use rsphp_macros_rs::ZEND_ARG_INFO_FOR_FUNCTION; - use rsphp_sys::{zend_class_entry, zend_function_entry, zend_internal_arg_info, zend_register_internal_class_with_flags}; + use rsphp_sys::{zend_class_entry, zend_function_entry, zend_internal_arg_info}; #[cfg(debug_assertions)] use rsphp_sys::ZEND_ACC_PUBLIC; #[cfg(debug_assertions)] @@ -238,9 +237,9 @@ pub(crate) mod Demo_InDemoNamespace { assert_eq!(2, methods.len(), "Demo\\InDemoNamespace should have 1 methods"); let mut ce: zend_class_entry = unsafe { std::mem::zeroed() }; - INIT_NS_CLASS_ENTRY!(ce, "Demo", "InDemoNamespace", methods); + rsphp_macros_c::INIT_NS_CLASS_ENTRY!(ce, "Demo", "InDemoNamespace", methods); let class_entry: *mut zend_class_entry = unsafe { - zend_register_internal_class_with_flags( + rsphp_sys::zend_register_internal_class_with_flags( std::ptr::from_mut::(&mut ce), std::ptr::null_mut(), 0, diff --git a/crates/stub-parser/tests/verification-cases/no_methods.expected b/crates/stub-parser/tests/verification-cases/no_methods.expected index 46640f7..2c3f968 100644 --- a/crates/stub-parser/tests/verification-cases/no_methods.expected +++ b/crates/stub-parser/tests/verification-cases/no_methods.expected @@ -1,16 +1,16 @@ /// Module for `Example` class. pub(crate) mod Example { - use rsphp_macros_c::{INIT_CLASS_ENTRY, ZEND_TYPE_INIT_MASK, ZVAL_UNDEF}; - use rsphp_sys::{MAY_BE_STRING, ZEND_ACC_FINAL, ZEND_ACC_NOT_SERIALIZABLE, ZEND_ACC_NO_DYNAMIC_PROPERTIES, ZEND_ACC_PUBLIC, zend_class_entry, zend_declare_typed_property, zend_register_internal_class_with_flags, zend_string, zend_string_init, zend_string_release, zval}; + use rsphp_macros_c::{ZEND_TYPE_INIT_MASK, ZVAL_UNDEF}; + use rsphp_sys::{MAY_BE_STRING, ZEND_ACC_FINAL, ZEND_ACC_NOT_SERIALIZABLE, ZEND_ACC_NO_DYNAMIC_PROPERTIES, ZEND_ACC_PUBLIC, zend_class_entry, zend_declare_typed_property, zend_string, zend_string_init, zend_string_release, zval}; /// Register the `Example` class. pub(crate) fn register_class() -> *mut zend_class_entry { let mut ce: zend_class_entry = unsafe { std::mem::zeroed() }; - INIT_CLASS_ENTRY!(ce, "Example", NULL); + rsphp_macros_c::INIT_CLASS_ENTRY!(ce, "Example", NULL); let class_entry: *mut zend_class_entry = unsafe { - zend_register_internal_class_with_flags( + rsphp_sys::zend_register_internal_class_with_flags( std::ptr::from_mut::(&mut ce), std::ptr::null_mut(), ZEND_ACC_FINAL|ZEND_ACC_NO_DYNAMIC_PROPERTIES|ZEND_ACC_NOT_SERIALIZABLE, @@ -68,13 +68,13 @@ pub(crate) mod Example { } /// Module for `MyBoolean` enum. pub(crate) mod MyBoolean { - use rsphp_sys::{IS_UNDEF, zend_class_entry, zend_enum_add_case_cstr, zend_register_internal_enum}; + use rsphp_sys::{IS_UNDEF, zend_class_entry, zend_enum_add_case_cstr}; /// Register the `MyBoolean` enum. pub(crate) fn register_enum() -> *mut zend_class_entry { let class_entry: *mut zend_class_entry = unsafe { - zend_register_internal_enum( + rsphp_sys::zend_register_internal_enum( c"MyBoolean".as_ptr(), IS_UNDEF, std::ptr::null_mut(), @@ -91,16 +91,15 @@ pub(crate) mod MyBoolean { } /// Module for `Marker` interface. pub(crate) mod Marker { - use rsphp_macros_c::INIT_CLASS_ENTRY; - use rsphp_sys::{zend_class_entry, zend_register_internal_interface}; + use rsphp_sys::zend_class_entry; /// Register the `Marker` interface. pub(crate) fn register_interface() -> *mut zend_class_entry { let mut ce: zend_class_entry = unsafe { std::mem::zeroed() }; - INIT_CLASS_ENTRY!(ce, "Marker", NULL); + rsphp_macros_c::INIT_CLASS_ENTRY!(ce, "Marker", NULL); let class_entry: *mut zend_class_entry = unsafe { - zend_register_internal_interface( + rsphp_sys::zend_register_internal_interface( std::ptr::from_mut::(&mut ce), ) }; From efa6cc3de4f5ed78a2d0372ec56e25d5729be98c Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Mon, 12 Oct 2026 15:23:15 -0700 Subject: [PATCH 10/12] CI: Try to verify generated files --- .github/actions/verify-generated/action.yml | 21 +++++++++++++++++++++ .github/workflows/test.yml | 9 +++++++++ 2 files changed, 30 insertions(+) create mode 100644 .github/actions/verify-generated/action.yml diff --git a/.github/actions/verify-generated/action.yml b/.github/actions/verify-generated/action.yml new file mode 100644 index 0000000..1317ce2 --- /dev/null +++ b/.github/actions/verify-generated/action.yml @@ -0,0 +1,21 @@ +name: verify-generated +inputs: + working-directory: + required: true +runs: + using: composite + steps: + - name: Run git add for untracked files + shell: bash + run: git add -N . + working-directory: ${{ inputs.working-directory }} + + - name: Show git status + shell: bash + run: git status + working-directory: ${{ inputs.working-directory }} + + - name: Check for changed files + shell: bash + run: git diff -a --exit-code HEAD + working-directory: ${{ inputs.working-directory }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index b6f4484..999208e 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -83,9 +83,18 @@ jobs: working-directory: ./php/php-src/ext/rust-ext - name: Run clippy on example + # Run clippy for the example even if for the main project it failed + if: ${{ !cancelled() }} run: cargo clippy --all-targets --all-features -- -Dwarnings working-directory: ./php/php-src/ext/rust-ext/example/rust-ext-demo + - name: Check generated files + # Run check even if clippy or something else failed + if: ${{ !cancelled() }} + uses: ./php/php-src/ext/rust-ext/.github/actions/verify-generated + with: + working-directory: php/php-src/ext/rust-ext + test: name: > Run tests (${{ matrix.php }}, From c47d96ed5a257c2ea595d840036a2eacd0089cd9 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Mon, 12 Oct 2026 15:36:08 -0700 Subject: [PATCH 11/12] Ignore changes to bindings, update example extension --- .github/actions/verify-generated/action.yml | 4 ++- .github/workflows/test.yml | 1 + example/rust-ext-demo/Cargo.lock | 12 ++++----- example/rust-ext-demo/src/ini_demo_arginfo.rs | 22 ++++++++-------- example/rust-ext-demo/src/lib.rs | 23 ---------------- .../rust-ext-demo/src/opaquedata_arginfo.rs | 23 ++++++++-------- example/rust-ext-demo/src/rustdemo_arginfo.rs | 26 +++++++++++-------- 7 files changed, 48 insertions(+), 63 deletions(-) diff --git a/.github/actions/verify-generated/action.yml b/.github/actions/verify-generated/action.yml index 1317ce2..9d8773c 100644 --- a/.github/actions/verify-generated/action.yml +++ b/.github/actions/verify-generated/action.yml @@ -2,6 +2,8 @@ name: verify-generated inputs: working-directory: required: true + extra-pathspec: + required: true runs: using: composite steps: @@ -17,5 +19,5 @@ runs: - name: Check for changed files shell: bash - run: git diff -a --exit-code HEAD + run: git diff -a --exit-code HEAD -- ${{ inputs.extra-pathspec }} working-directory: ${{ inputs.working-directory }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 999208e..3aa6cb0 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -94,6 +94,7 @@ jobs: uses: ./php/php-src/ext/rust-ext/.github/actions/verify-generated with: working-directory: php/php-src/ext/rust-ext + extra-pathspec: ':(exclude)crates/sys/bindings.rs' test: name: > diff --git a/example/rust-ext-demo/Cargo.lock b/example/rust-ext-demo/Cargo.lock index 9088a26..03f21db 100644 --- a/example/rust-ext-demo/Cargo.lock +++ b/example/rust-ext-demo/Cargo.lock @@ -490,7 +490,7 @@ checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4" [[package]] name = "rsphp" -version = "0.1.0" +version = "0.1.1" dependencies = [ "bstr", "rsphp-macros-c", @@ -500,7 +500,7 @@ dependencies = [ [[package]] name = "rsphp-build-tools" -version = "0.1.0" +version = "0.1.1" dependencies = [ "bindgen", ] @@ -519,7 +519,7 @@ dependencies = [ [[package]] name = "rsphp-macros-c" -version = "0.1.0" +version = "0.1.1" dependencies = [ "c_str_macro", "rsphp-sys", @@ -527,7 +527,7 @@ dependencies = [ [[package]] name = "rsphp-macros-rs" -version = "0.1.0" +version = "0.1.1" dependencies = [ "rsphp-macros-c", "rsphp-sys", @@ -535,7 +535,7 @@ dependencies = [ [[package]] name = "rsphp-stub-parser" -version = "0.1.0" +version = "0.1.1" dependencies = [ "php-ast", "php-rs-parser", @@ -544,7 +544,7 @@ dependencies = [ [[package]] name = "rsphp-sys" -version = "0.1.0" +version = "0.1.1" dependencies = [ "bindgen", "cc", diff --git a/example/rust-ext-demo/src/ini_demo_arginfo.rs b/example/rust-ext-demo/src/ini_demo_arginfo.rs index e456962..6bd1c30 100644 --- a/example/rust-ext-demo/src/ini_demo_arginfo.rs +++ b/example/rust-ext-demo/src/ini_demo_arginfo.rs @@ -1,6 +1,5 @@ -use rsphp_macros_c::*; -use rsphp_macros_rs::*; -use rsphp_sys::*; +#[cfg(debug_assertions)] +use rsphp_sys::{zend_function_entry, zend_internal_arg_info, zend_string, zend_type, zend_type_list}; #[cfg(debug_assertions)] use std::ffi::{c_char, CStr}; @@ -40,7 +39,7 @@ fn types_match(expected: &zend_type, actual: &zend_type) -> Option { return Some("Expected a pointer to be set in the actual type".to_owned()); } // Compare the values depending on the flags - if expected.type_mask & _ZEND_TYPE_LITERAL_NAME_BIT != 0 { + if expected.type_mask & rsphp_sys::_ZEND_TYPE_LITERAL_NAME_BIT != 0 { // CString pointers let expected_name = unsafe { CStr::from_ptr(expected.ptr.cast::()) }; let actual_name = unsafe { CStr::from_ptr(actual.ptr.cast::()) }; @@ -49,7 +48,7 @@ fn types_match(expected: &zend_type, actual: &zend_type) -> Option { } return Some(format!("Expected literal name {expected_name:?}, found {actual_name:?}")); } - if expected.type_mask & _ZEND_TYPE_NAME_BIT != 0 { + if expected.type_mask & rsphp_sys::_ZEND_TYPE_NAME_BIT != 0 { // zend_string pointers, compare them by comparing the lengths and values let expected_name: &zend_string = unsafe { expected.ptr.cast::().as_ref().expect("Valid pointer") }; let actual_name: &zend_string = unsafe { actual.ptr.cast::().as_ref().expect("Valid pointer") }; @@ -63,7 +62,7 @@ fn types_match(expected: &zend_type, actual: &zend_type) -> Option { } return Some(format!("Expected name {expected_slice:?}, found {actual_slice:?}")); } - if expected.type_mask & _ZEND_TYPE_LIST_BIT != 0 { + if expected.type_mask & rsphp_sys::_ZEND_TYPE_LIST_BIT != 0 { // zend_type_list pointers let expected_list: &zend_type_list = unsafe { expected.ptr.cast::().as_ref().expect("Valid pointer") }; let acutal_list: &zend_type_list = unsafe { actual.ptr.cast::().as_ref().expect("Valid pointer") }; @@ -210,9 +209,10 @@ fn verify_registration( } /// Module for `IniDemo` class. pub(crate) mod IniDemo { - use rsphp_macros_c::*; - use rsphp_macros_rs::*; - use rsphp_sys::*; + use rsphp_macros_rs::ZEND_ARG_INFO_FOR_FUNCTION_WITH_RETURN_TYPE_MASK; + use rsphp_sys::{MAY_BE_BOOL, MAY_BE_DOUBLE, MAY_BE_LONG, MAY_BE_STRING, zend_class_entry, zend_function_entry, zend_internal_arg_info}; + #[cfg(debug_assertions)] + use rsphp_sys::{ZEND_ACC_PUBLIC, ZEND_ACC_STATIC}; #[cfg(debug_assertions)] use super::verify_registration; @@ -259,9 +259,9 @@ pub(crate) mod IniDemo { assert_eq!(6, methods.len(), "IniDemo should have 5 methods"); let mut ce: zend_class_entry = unsafe { std::mem::zeroed() }; - INIT_CLASS_ENTRY!(ce, "IniDemo", methods); + rsphp_macros_c::INIT_CLASS_ENTRY!(ce, "IniDemo", methods); let class_entry: *mut zend_class_entry = unsafe { - zend_register_internal_class_with_flags( + rsphp_sys::zend_register_internal_class_with_flags( std::ptr::from_mut::(&mut ce), std::ptr::null_mut(), 0, diff --git a/example/rust-ext-demo/src/lib.rs b/example/rust-ext-demo/src/lib.rs index 9cafb89..b388b34 100644 --- a/example/rust-ext-demo/src/lib.rs +++ b/example/rust-ext-demo/src/lib.rs @@ -29,34 +29,11 @@ mod ini_demo; use ini_demo::{class_ini_demo_methods, ini_demo_ptr}; #[rustfmt::skip] -#[cfg_attr( - not(test), - expect( - clippy::wildcard_imports, - reason = "rust-lang/rust-clippy#13186" - ) -)] #[expect(clippy::cast_possible_wrap)] mod rustdemo_arginfo; #[rustfmt::skip] -#[cfg_attr( - not(test), - expect( - clippy::wildcard_imports, - reason = "rust-lang/rust-clippy#13186" - ) -)] -#[expect(unused_imports)] mod opaquedata_arginfo; #[rustfmt::skip] -#[cfg_attr( - not(test), - expect( - clippy::wildcard_imports, - reason = "rust-lang/rust-clippy#13186" - ) -)] -#[expect(unused)] mod ini_demo_arginfo; extern "C" fn my_module_info(entry: *mut zend_module_entry) { diff --git a/example/rust-ext-demo/src/opaquedata_arginfo.rs b/example/rust-ext-demo/src/opaquedata_arginfo.rs index f39a763..a5fd63a 100644 --- a/example/rust-ext-demo/src/opaquedata_arginfo.rs +++ b/example/rust-ext-demo/src/opaquedata_arginfo.rs @@ -1,6 +1,5 @@ -use rsphp_macros_c::*; -use rsphp_macros_rs::*; -use rsphp_sys::*; +#[cfg(debug_assertions)] +use rsphp_sys::{zend_function_entry, zend_internal_arg_info, zend_string, zend_type, zend_type_list}; #[cfg(debug_assertions)] use std::ffi::{c_char, CStr}; @@ -40,7 +39,7 @@ fn types_match(expected: &zend_type, actual: &zend_type) -> Option { return Some("Expected a pointer to be set in the actual type".to_owned()); } // Compare the values depending on the flags - if expected.type_mask & _ZEND_TYPE_LITERAL_NAME_BIT != 0 { + if expected.type_mask & rsphp_sys::_ZEND_TYPE_LITERAL_NAME_BIT != 0 { // CString pointers let expected_name = unsafe { CStr::from_ptr(expected.ptr.cast::()) }; let actual_name = unsafe { CStr::from_ptr(actual.ptr.cast::()) }; @@ -49,7 +48,7 @@ fn types_match(expected: &zend_type, actual: &zend_type) -> Option { } return Some(format!("Expected literal name {expected_name:?}, found {actual_name:?}")); } - if expected.type_mask & _ZEND_TYPE_NAME_BIT != 0 { + if expected.type_mask & rsphp_sys::_ZEND_TYPE_NAME_BIT != 0 { // zend_string pointers, compare them by comparing the lengths and values let expected_name: &zend_string = unsafe { expected.ptr.cast::().as_ref().expect("Valid pointer") }; let actual_name: &zend_string = unsafe { actual.ptr.cast::().as_ref().expect("Valid pointer") }; @@ -63,7 +62,7 @@ fn types_match(expected: &zend_type, actual: &zend_type) -> Option { } return Some(format!("Expected name {expected_slice:?}, found {actual_slice:?}")); } - if expected.type_mask & _ZEND_TYPE_LIST_BIT != 0 { + if expected.type_mask & rsphp_sys::_ZEND_TYPE_LIST_BIT != 0 { // zend_type_list pointers let expected_list: &zend_type_list = unsafe { expected.ptr.cast::().as_ref().expect("Valid pointer") }; let acutal_list: &zend_type_list = unsafe { actual.ptr.cast::().as_ref().expect("Valid pointer") }; @@ -210,9 +209,11 @@ fn verify_registration( } /// Module for `RustDemo\OpaqueData` class. pub(crate) mod RustDemo_OpaqueData { - use rsphp_macros_c::*; - use rsphp_macros_rs::*; - use rsphp_sys::*; + use rsphp_macros_c::ZEND_ARG_TYPE_INFO; + use rsphp_macros_rs::{ZEND_ARG_INFO_FOR_FUNCTION, ZEND_ARG_INFO_FOR_FUNCTION_WITH_RETURN_TYPE_MASK}; + use rsphp_sys::{MAY_BE_BOOL, MAY_BE_VOID, _IS_BOOL, zend_class_entry, zend_function_entry, zend_internal_arg_info}; + #[cfg(debug_assertions)] + use rsphp_sys::ZEND_ACC_PUBLIC; #[cfg(debug_assertions)] use super::verify_registration; @@ -254,9 +255,9 @@ pub(crate) mod RustDemo_OpaqueData { assert_eq!(5, methods.len(), "RustDemo\\OpaqueData should have 4 methods"); let mut ce: zend_class_entry = unsafe { std::mem::zeroed() }; - INIT_NS_CLASS_ENTRY!(ce, "RustDemo", "OpaqueData", methods); + rsphp_macros_c::INIT_NS_CLASS_ENTRY!(ce, "RustDemo", "OpaqueData", methods); let class_entry: *mut zend_class_entry = unsafe { - zend_register_internal_class_with_flags( + rsphp_sys::zend_register_internal_class_with_flags( std::ptr::from_mut::(&mut ce), std::ptr::null_mut(), 0, diff --git a/example/rust-ext-demo/src/rustdemo_arginfo.rs b/example/rust-ext-demo/src/rustdemo_arginfo.rs index 2f4bf73..c98f3e8 100644 --- a/example/rust-ext-demo/src/rustdemo_arginfo.rs +++ b/example/rust-ext-demo/src/rustdemo_arginfo.rs @@ -1,6 +1,8 @@ -use rsphp_macros_c::*; -use rsphp_macros_rs::*; -use rsphp_sys::*; +use rsphp_macros_c::{ZEND_ARG_INFO, ZEND_ARG_TYPE_INFO, ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE, ZEND_ARG_VARIADIC_TYPE_INFO}; +use rsphp_macros_rs::ZEND_ARG_INFO_FOR_FUNCTION_WITH_RETURN_TYPE_MASK; +use rsphp_sys::{IS_DOUBLE, IS_LONG, IS_MIXED, IS_STRING, MAY_BE_STRING, MAY_BE_VOID, _IS_BOOL, zend_internal_arg_info}; +#[cfg(debug_assertions)] +use rsphp_sys::{zend_function_entry, zend_string, zend_type, zend_type_list}; #[cfg(debug_assertions)] use std::ffi::{c_char, CStr}; @@ -40,7 +42,7 @@ fn types_match(expected: &zend_type, actual: &zend_type) -> Option { return Some("Expected a pointer to be set in the actual type".to_owned()); } // Compare the values depending on the flags - if expected.type_mask & _ZEND_TYPE_LITERAL_NAME_BIT != 0 { + if expected.type_mask & rsphp_sys::_ZEND_TYPE_LITERAL_NAME_BIT != 0 { // CString pointers let expected_name = unsafe { CStr::from_ptr(expected.ptr.cast::()) }; let actual_name = unsafe { CStr::from_ptr(actual.ptr.cast::()) }; @@ -49,7 +51,7 @@ fn types_match(expected: &zend_type, actual: &zend_type) -> Option { } return Some(format!("Expected literal name {expected_name:?}, found {actual_name:?}")); } - if expected.type_mask & _ZEND_TYPE_NAME_BIT != 0 { + if expected.type_mask & rsphp_sys::_ZEND_TYPE_NAME_BIT != 0 { // zend_string pointers, compare them by comparing the lengths and values let expected_name: &zend_string = unsafe { expected.ptr.cast::().as_ref().expect("Valid pointer") }; let actual_name: &zend_string = unsafe { actual.ptr.cast::().as_ref().expect("Valid pointer") }; @@ -63,7 +65,7 @@ fn types_match(expected: &zend_type, actual: &zend_type) -> Option { } return Some(format!("Expected name {expected_slice:?}, found {actual_slice:?}")); } - if expected.type_mask & _ZEND_TYPE_LIST_BIT != 0 { + if expected.type_mask & rsphp_sys::_ZEND_TYPE_LIST_BIT != 0 { // zend_type_list pointers let expected_list: &zend_type_list = unsafe { expected.ptr.cast::().as_ref().expect("Valid pointer") }; let acutal_list: &zend_type_list = unsafe { actual.ptr.cast::().as_ref().expect("Valid pointer") }; @@ -266,9 +268,11 @@ pub(crate) const ARGINFO_DISPLAY_VALUE: [zend_internal_arg_info; 2] = [ /// Module for `RSDemo` class. pub(crate) mod RSDemo { - use rsphp_macros_c::*; - use rsphp_macros_rs::*; - use rsphp_sys::*; + use rsphp_macros_c::{ZEND_ARG_TYPE_INFO, ZEND_TYPE_INIT_MASK, ZVAL_UNDEF}; + use rsphp_macros_rs::{ZEND_ARG_INFO_FOR_FUNCTION, ZEND_ARG_INFO_FOR_FUNCTION_WITH_RETURN_TYPE_MASK}; + use rsphp_sys::{IS_LONG, MAY_BE_LONG, MAY_BE_STRING, MAY_BE_VOID, ZEND_ACC_PRIVATE, zend_class_entry, zend_declare_typed_property, zend_function_entry, zend_internal_arg_info, zend_string, zend_string_init, zend_string_release, zval}; + #[cfg(debug_assertions)] + use rsphp_sys::ZEND_ACC_PUBLIC; #[cfg(debug_assertions)] use super::verify_registration; @@ -304,9 +308,9 @@ pub(crate) mod RSDemo { assert_eq!(4, methods.len(), "RSDemo should have 3 methods"); let mut ce: zend_class_entry = unsafe { std::mem::zeroed() }; - INIT_CLASS_ENTRY!(ce, "RSDemo", methods); + rsphp_macros_c::INIT_CLASS_ENTRY!(ce, "RSDemo", methods); let class_entry: *mut zend_class_entry = unsafe { - zend_register_internal_class_with_flags( + rsphp_sys::zend_register_internal_class_with_flags( std::ptr::from_mut::(&mut ce), std::ptr::null_mut(), 0, From c4432532ae1d96249c79188d2bf7a1361ce126ca Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Mon, 12 Oct 2026 15:45:58 -0700 Subject: [PATCH 12/12] quotes --- .github/actions/verify-generated/action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/verify-generated/action.yml b/.github/actions/verify-generated/action.yml index 9d8773c..feb02ab 100644 --- a/.github/actions/verify-generated/action.yml +++ b/.github/actions/verify-generated/action.yml @@ -19,5 +19,5 @@ runs: - name: Check for changed files shell: bash - run: git diff -a --exit-code HEAD -- ${{ inputs.extra-pathspec }} + run: git diff -a --exit-code HEAD -- "${{ inputs.extra-pathspec }}" working-directory: ${{ inputs.working-directory }}