Skip to content

Commit 4dd7b8d

Browse files
authored
Merge pull request #230 from davidcole1340/function-arg-pass-by-ref
Support function args being passed by reference
2 parents 7d484a0 + 64bf9e7 commit 4dd7b8d

File tree

2 files changed

+65
-3
lines changed

2 files changed

+65
-3
lines changed

crates/macros/src/function.rs

Lines changed: 51 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ pub struct Arg {
2626
pub ty: String,
2727
pub nullable: bool,
2828
pub default: Option<String>,
29+
pub as_ref: bool,
2930
}
3031

3132
#[derive(Debug, Clone)]
@@ -249,12 +250,19 @@ pub fn get_return_type(output_type: &ReturnType) -> Result<Option<(String, bool)
249250
}
250251

251252
impl Arg {
252-
pub fn new(name: String, ty: String, nullable: bool, default: Option<String>) -> Self {
253+
pub fn new(
254+
name: String,
255+
ty: String,
256+
nullable: bool,
257+
default: Option<String>,
258+
as_ref: bool,
259+
) -> Self {
253260
Self {
254261
name,
255262
ty,
256263
nullable,
257264
default,
265+
as_ref,
258266
}
259267
}
260268

@@ -268,6 +276,7 @@ impl Arg {
268276
match ty {
269277
Type::Path(TypePath { path, .. }) => {
270278
let mut path = path.clone();
279+
let mut pass_by_ref = false;
271280
path.drop_lifetimes();
272281

273282
let seg = path.segments.last()?;
@@ -283,16 +292,53 @@ impl Arg {
283292
None
284293
}
285294
});
295+
296+
// For for types that are `Option<&mut T>` to turn them into `Option<&T>`,
297+
// marking the Arg as as "passed by reference".
298+
let option = Some(seg)
299+
.filter(|seg| seg.ident == "Option")
300+
.and_then(|seg| {
301+
if let PathArguments::AngleBracketed(args) = &seg.arguments {
302+
args.args
303+
.iter()
304+
.find(|arg| matches!(arg, GenericArgument::Type(_)))
305+
.map(|ga| {
306+
let new_ga = match ga {
307+
GenericArgument::Type(ty) => {
308+
let _rtype = match ty {
309+
Type::Reference(r) => {
310+
let mut new_ref = r.clone();
311+
new_ref.mutability = None;
312+
pass_by_ref = true;
313+
Type::Reference(new_ref)
314+
}
315+
_ => ty.clone(),
316+
};
317+
GenericArgument::Type(_rtype)
318+
}
319+
_ => ga.clone(),
320+
};
321+
new_ga.to_token_stream().to_string()
322+
})
323+
} else {
324+
None
325+
}
326+
});
327+
286328
let stringified = match result {
287329
Some(result) if is_return => result,
288-
_ => path.to_token_stream().to_string(),
330+
_ => match option {
331+
Some(result) => result,
332+
None => path.to_token_stream().to_string(),
333+
},
289334
};
290335

291336
Some(Arg::new(
292337
name,
293338
stringified,
294339
seg.ident == "Option" || default.is_some(),
295340
default,
341+
pass_by_ref,
296342
))
297343
}
298344
Type::Reference(ref_) => {
@@ -302,6 +348,7 @@ impl Arg {
302348
ref_.to_token_stream().to_string(),
303349
false,
304350
default,
351+
ref_.mutability.is_some(),
305352
))
306353
}
307354
_ => None,
@@ -361,14 +408,15 @@ impl Arg {
361408
let ty = self.get_type_ident();
362409

363410
let null = self.nullable.then(|| quote! { .allow_null() });
411+
let passed_by_ref = self.as_ref.then(|| quote! { .as_ref() });
364412
let default = self.default.as_ref().map(|val| {
365413
quote! {
366414
.default(#val)
367415
}
368416
});
369417

370418
quote! {
371-
::ext_php_rs::args::Arg::new(#name, #ty) #null #default
419+
::ext_php_rs::args::Arg::new(#name, #ty) #null #passed_by_ref #default
372420
}
373421
}
374422
}

guide/src/types/bool.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,3 +45,17 @@ pub fn test_bool(input: bool) -> String {
4545
var_dump(test_bool(true)); // string(4) "Yes!"
4646
var_dump(test_bool(false)); // string(3) "No!"
4747
```
48+
49+
## Rust example, taking by reference
50+
51+
```rust,no_run
52+
# #![cfg_attr(windows, feature(abi_vectorcall))]
53+
# extern crate ext_php_rs;
54+
# use ext_php_rs::prelude::*;
55+
# use ext_php_rs::types;
56+
#[php_function]
57+
pub fn test_bool(input: &mut types::Zval) {
58+
input.reference_mut().unwrap().set_bool(false);
59+
}
60+
# fn main() {}
61+
```

0 commit comments

Comments
 (0)