Merge pull request #230 from davidcole1340/function-arg-pass-by-ref

Support function args being passed by reference
This commit is contained in:
Daniil Gentili 2023-10-10 16:13:31 +02:00 committed by GitHub
commit 4dd7b8ded5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 65 additions and 3 deletions

View File

@ -26,6 +26,7 @@ pub struct Arg {
pub ty: String,
pub nullable: bool,
pub default: Option<String>,
pub as_ref: bool,
}
#[derive(Debug, Clone)]
@ -249,12 +250,19 @@ pub fn get_return_type(output_type: &ReturnType) -> Result<Option<(String, bool)
}
impl Arg {
pub fn new(name: String, ty: String, nullable: bool, default: Option<String>) -> Self {
pub fn new(
name: String,
ty: String,
nullable: bool,
default: Option<String>,
as_ref: bool,
) -> Self {
Self {
name,
ty,
nullable,
default,
as_ref,
}
}
@ -268,6 +276,7 @@ impl Arg {
match ty {
Type::Path(TypePath { path, .. }) => {
let mut path = path.clone();
let mut pass_by_ref = false;
path.drop_lifetimes();
let seg = path.segments.last()?;
@ -283,9 +292,45 @@ impl Arg {
None
}
});
// For for types that are `Option<&mut T>` to turn them into `Option<&T>`,
// marking the Arg as as "passed by reference".
let option = Some(seg)
.filter(|seg| seg.ident == "Option")
.and_then(|seg| {
if let PathArguments::AngleBracketed(args) = &seg.arguments {
args.args
.iter()
.find(|arg| matches!(arg, GenericArgument::Type(_)))
.map(|ga| {
let new_ga = match ga {
GenericArgument::Type(ty) => {
let _rtype = match ty {
Type::Reference(r) => {
let mut new_ref = r.clone();
new_ref.mutability = None;
pass_by_ref = true;
Type::Reference(new_ref)
}
_ => ty.clone(),
};
GenericArgument::Type(_rtype)
}
_ => ga.clone(),
};
new_ga.to_token_stream().to_string()
})
} else {
None
}
});
let stringified = match result {
Some(result) if is_return => result,
_ => path.to_token_stream().to_string(),
_ => match option {
Some(result) => result,
None => path.to_token_stream().to_string(),
},
};
Some(Arg::new(
@ -293,6 +338,7 @@ impl Arg {
stringified,
seg.ident == "Option" || default.is_some(),
default,
pass_by_ref,
))
}
Type::Reference(ref_) => {
@ -302,6 +348,7 @@ impl Arg {
ref_.to_token_stream().to_string(),
false,
default,
ref_.mutability.is_some(),
))
}
_ => None,
@ -361,6 +408,7 @@ impl Arg {
let ty = self.get_type_ident();
let null = self.nullable.then(|| quote! { .allow_null() });
let passed_by_ref = self.as_ref.then(|| quote! { .as_ref() });
let default = self.default.as_ref().map(|val| {
quote! {
.default(#val)
@ -368,7 +416,7 @@ impl Arg {
});
quote! {
::ext_php_rs::args::Arg::new(#name, #ty) #null #default
::ext_php_rs::args::Arg::new(#name, #ty) #null #passed_by_ref #default
}
}
}

View File

@ -45,3 +45,17 @@ pub fn test_bool(input: bool) -> String {
var_dump(test_bool(true)); // string(4) "Yes!"
var_dump(test_bool(false)); // string(3) "No!"
```
## Rust example, taking by reference
```rust,no_run
# #![cfg_attr(windows, feature(abi_vectorcall))]
# extern crate ext_php_rs;
# use ext_php_rs::prelude::*;
# use ext_php_rs::types;
#[php_function]
pub fn test_bool(input: &mut types::Zval) {
input.reference_mut().unwrap().set_bool(false);
}
# fn main() {}
```