Skip to content

Commit 754a57b

Browse files
committed
wip: outer macro pattern for functions
1 parent c150a54 commit 754a57b

File tree

8 files changed

+192
-135
lines changed

8 files changed

+192
-135
lines changed

crates/macros/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,6 @@ quote = "1.0.9"
1919
proc-macro2 = "1.0.26"
2020
lazy_static = "1.4.0"
2121
anyhow = "1.0"
22+
23+
[lints.rust]
24+
missing_docs = "warn"

crates/macros/src/class.rs

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
use std::collections::HashMap;
22

3-
use crate::STATE;
43
use anyhow::{anyhow, bail, Context, Result};
54
use darling::{FromMeta, ToTokens};
65
use proc_macro2::{Ident, Span, TokenStream};
@@ -132,17 +131,17 @@ pub fn parser(args: AttributeArgs, mut input: ItemStruct) -> Result<TokenStream>
132131
..Default::default()
133132
};
134133

135-
let mut state = STATE.lock();
136-
137-
if state.built_module {
138-
bail!("The `#[php_module]` macro must be called last to ensure functions and classes are registered.");
139-
}
140-
141-
if state.startup_function.is_some() {
142-
bail!("The `#[php_startup]` macro must be called after all the classes have been defined.");
143-
}
144-
145-
state.classes.insert(ident.to_string(), class);
134+
// let mut state = STATE.lock();
135+
//
136+
// if state.built_module {
137+
// bail!("The `#[php_module]` macro must be called last to ensure functions and classes are registered.");
138+
// }
139+
//
140+
// if state.startup_function.is_some() {
141+
// bail!("The `#[php_startup]` macro must be called after all the classes have been defined.");
142+
// }
143+
//
144+
// state.classes.insert(ident.to_string(), class);
146145

147146
Ok(quote! {
148147
#input

crates/macros/src/constant.rs

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@ use proc_macro2::TokenStream;
55
use quote::quote;
66
use syn::{Expr, ItemConst};
77

8-
use crate::STATE;
9-
108
#[derive(Debug)]
119
pub struct Constant {
1210
pub name: String,
@@ -16,17 +14,17 @@ pub struct Constant {
1614
}
1715

1816
pub fn parser(input: ItemConst) -> Result<TokenStream> {
19-
let mut state = STATE.lock();
20-
21-
if state.startup_function.is_some() {
22-
bail!("Constants must be declared before you declare your startup function and module function.");
23-
}
24-
25-
state.constants.push(Constant {
26-
name: input.ident.to_string(),
27-
docs: get_docs(&input.attrs),
28-
value: input.expr.to_token_stream().to_string(),
29-
});
17+
// let mut state = STATE.lock();
18+
//
19+
// if state.startup_function.is_some() {
20+
// bail!("Constants must be declared before you declare your startup function and module function.");
21+
// }
22+
//
23+
// state.constants.push(Constant {
24+
// name: input.ident.to_string(),
25+
// docs: get_docs(&input.attrs),
26+
// value: input.expr.to_token_stream().to_string(),
27+
// });
3028

3129
Ok(quote! {
3230
#[allow(dead_code)]

crates/macros/src/function.rs

Lines changed: 7 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use std::collections::HashMap;
22

33
use crate::helpers::get_docs;
4-
use crate::{syn_ext::DropLifetimes, STATE};
4+
use crate::syn_ext::DropLifetimes;
55
use anyhow::{anyhow, bail, Result};
66
use darling::{FromMeta, ToTokens};
77
use proc_macro2::{Ident, Literal, Span, TokenStream};
@@ -40,13 +40,8 @@ pub struct Function {
4040
pub output: Option<(String, bool)>,
4141
}
4242

43-
pub fn parser(args: AttributeArgs, input: ItemFn) -> Result<(TokenStream, Function)> {
44-
let attr_args = match AttrArgs::from_list(&args) {
45-
Ok(args) => args,
46-
Err(e) => bail!("Unable to parse attribute arguments: {:?}", e),
47-
};
48-
49-
let ItemFn { sig, .. } = &input;
43+
pub fn parser(attr_args: AttrArgs, input: &ItemFn) -> Result<(TokenStream, Function)> {
44+
let ItemFn { sig, block, .. } = &input;
5045
let Signature {
5146
ident,
5247
output,
@@ -69,7 +64,8 @@ pub fn parser(args: AttributeArgs, input: ItemFn) -> Result<(TokenStream, Functi
6964
let return_type = get_return_type(output)?;
7065

7166
let func = quote! {
72-
#input
67+
#sig
68+
#block
7369

7470
::ext_php_rs::zend_fastcall! {
7571
#[doc(hidden)]
@@ -89,12 +85,6 @@ pub fn parser(args: AttributeArgs, input: ItemFn) -> Result<(TokenStream, Functi
8985
}
9086
};
9187

92-
let mut state = STATE.lock();
93-
94-
if state.built_module && !attr_args.ignore_module {
95-
bail!("The `#[php_module]` macro must be called last to ensure functions are registered. To ignore this error, pass the `ignore_module` option into this attribute invocation: `#[php_function(ignore_module)]`");
96-
}
97-
9888
let function = Function {
9989
name: attr_args.name.unwrap_or_else(|| ident.to_string()),
10090
docs: get_docs(&input.attrs),
@@ -104,8 +94,6 @@ pub fn parser(args: AttributeArgs, input: ItemFn) -> Result<(TokenStream, Functi
10494
output: return_type,
10595
};
10696

107-
state.functions.push(function.clone());
108-
10997
Ok((func, function))
11098
}
11199

@@ -469,8 +457,9 @@ impl Function {
469457
}
470458
});
471459

460+
// TODO: fix module name
472461
quote! {
473-
::ext_php_rs::builders::FunctionBuilder::new(#name, #name_ident)
462+
::ext_php_rs::builders::FunctionBuilder::new(#name, module::#name_ident)
474463
#(#args)*
475464
#output
476465
.build()

crates/macros/src/impl_.rs

Lines changed: 30 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -105,31 +105,29 @@ pub fn parser(args: AttributeArgs, input: ItemImpl) -> Result<TokenStream> {
105105
bail!("This macro cannot be used on trait implementations.");
106106
}
107107

108-
let mut state = crate::STATE.lock();
109-
110-
if state.startup_function.is_some() {
111-
bail!(
112-
"Impls must be declared before you declare your startup function and module function."
113-
);
114-
}
115-
116-
let class = state.classes.get_mut(&class_name).ok_or_else(|| {
117-
anyhow!(
118-
"You must use `#[php_class]` on the struct before using this attribute on the impl."
119-
)
120-
})?;
108+
// if state.startup_function.is_some() {
109+
// bail!(
110+
// "Impls must be declared before you declare your startup function and module function."
111+
// );
112+
// }
113+
//
114+
// let class = state.classes.get_mut(&class_name).ok_or_else(|| {
115+
// anyhow!(
116+
// "You must use `#[php_class]` on the struct before using this attribute on the impl."
117+
// )
118+
// })?;
121119

122120
let tokens = items
123121
.into_iter()
124122
.map(|item| {
125123
Ok(match item {
126124
syn::ImplItem::Const(constant) => {
127-
class.constants.push(Constant {
128-
name: constant.ident.to_string(),
129-
// visibility: Visibility::Public,
130-
docs: get_docs(&constant.attrs),
131-
value: constant.expr.to_token_stream().to_string(),
132-
});
125+
// class.constants.push(Constant {
126+
// name: constant.ident.to_string(),
127+
// // visibility: Visibility::Public,
128+
// docs: get_docs(&constant.attrs),
129+
// value: constant.expr.to_token_stream().to_string(),
130+
// });
133131

134132
quote! {
135133
#[allow(dead_code)]
@@ -143,24 +141,24 @@ pub fn parser(args: AttributeArgs, input: ItemImpl) -> Result<TokenStream> {
143141
// TODO(david): How do we handle comments for getter/setter? Take the comments
144142
// from the methods??
145143
if let Some((prop, ty)) = parsed_method.property {
146-
let prop = class
147-
.properties
148-
.entry(prop)
149-
.or_insert_with(|| Property::method(vec![], None));
144+
// let prop = class
145+
// .properties
146+
// .entry(prop)
147+
// .or_insert_with(|| Property::method(vec![], None));
150148
let ident = parsed_method.method.orig_ident.clone();
151149

152-
match ty {
153-
PropAttrTy::Getter => prop.add_getter(ident)?,
154-
PropAttrTy::Setter => prop.add_setter(ident)?,
155-
}
150+
// match ty {
151+
// PropAttrTy::Getter => prop.add_getter(ident)?,
152+
// PropAttrTy::Setter => prop.add_setter(ident)?,
153+
// }
156154
}
157155
if parsed_method.constructor {
158-
if class.constructor.is_some() {
159-
bail!("You cannot have two constructors on the same class.");
160-
}
161-
class.constructor = Some(parsed_method.method);
156+
// if class.constructor.is_some() {
157+
// bail!("You cannot have two constructors on the same class.");
158+
// }
159+
// class.constructor = Some(parsed_method.method);
162160
} else {
163-
class.methods.push(parsed_method.method);
161+
// class.methods.push(parsed_method.method);
164162
}
165163
parsed_method.tokens
166164
}

crates/macros/src/lib.rs

Lines changed: 90 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
//! # Macros for PHP bindings
12
mod class;
23
mod constant;
34
mod extern_;
@@ -11,17 +12,16 @@ mod startup_function;
1112
mod syn_ext;
1213
mod zval;
1314

14-
use std::{
15-
collections::HashMap,
16-
sync::{Mutex, MutexGuard},
17-
};
15+
use std::collections::HashMap;
1816

1917
use constant::Constant;
18+
use darling::FromMeta;
2019
use proc_macro::TokenStream;
21-
use proc_macro2::Span;
20+
use proc_macro2::{Span, TokenStream as TokenStream2};
21+
use quote::{quote, ToTokens};
2222
use syn::{
23-
parse_macro_input, AttributeArgs, DeriveInput, ItemConst, ItemFn, ItemForeignMod, ItemImpl,
24-
ItemStruct,
23+
parse_macro_input, spanned::Spanned, Attribute, AttributeArgs, DeriveInput, Item, ItemConst,
24+
ItemFn, ItemForeignMod, ItemImpl, ItemMod, ItemStruct, NestedMeta,
2525
};
2626

2727
extern crate proc_macro;
@@ -32,22 +32,83 @@ struct State {
3232
classes: HashMap<String, class::Class>,
3333
constants: Vec<Constant>,
3434
startup_function: Option<String>,
35-
built_module: bool,
3635
}
3736

38-
lazy_static::lazy_static! {
39-
pub(crate) static ref STATE: StateMutex = StateMutex::new();
40-
}
37+
impl State {
38+
fn parse_item(&mut self, item: &Item) -> TokenStream2 {
39+
let mut unparsed_attr = vec![];
40+
let tokens: TokenStream2 = match item {
41+
Item::Fn(func) => {
42+
for attr in &func.attrs {
43+
if attr.style == syn::AttrStyle::Outer && attr.path.is_ident("php_function") {
44+
match Self::parse_attr(attr) {
45+
Ok(attr_args) => {
46+
let (tokens, func) = function::parser(attr_args, func)
47+
.expect("Failed to parse function");
48+
self.functions.push(func);
49+
return tokens;
50+
}
51+
Err(e) => return e,
52+
}
53+
} else if attr.style == syn::AttrStyle::Outer
54+
&& attr.path.is_ident("php_startup")
55+
{
56+
match Self::parse_attr(attr) {
57+
Ok(attr_args) => {
58+
let (tokens, func) =
59+
startup_function::parser(Some(attr_args), func)
60+
.expect("Failed to parse startup function");
61+
self.startup_function = Some(func.to_string());
62+
return tokens;
63+
}
64+
Err(e) => return e,
65+
}
66+
} else {
67+
unparsed_attr.push(attr.into_token_stream());
68+
}
69+
70+
unparsed_attr.push(attr.into_token_stream());
71+
}
72+
return quote! { #func };
73+
}
74+
_ => quote! { #item },
75+
};
76+
77+
tokens
78+
}
79+
80+
fn parse_attr<T>(attr: &Attribute) -> Result<T, TokenStream2>
81+
where
82+
T: FromMeta,
83+
{
84+
let meta = Self::parse_metadata(attr);
4185

42-
struct StateMutex(Mutex<State>);
86+
Self::parse_from_meta(&meta, Some(attr.span()))
87+
}
4388

44-
impl StateMutex {
45-
pub fn new() -> Self {
46-
Self(Mutex::new(Default::default()))
89+
fn parse_metadata(attr: &Attribute) -> Vec<NestedMeta> {
90+
if let Ok(args) = attr.parse_args::<TokenStream2>().map(|args| args.into()) {
91+
syn::parse_macro_input::parse::<AttributeArgs>(args).unwrap_or(vec![])
92+
} else {
93+
vec![]
94+
}
4795
}
4896

49-
pub fn lock(&self) -> MutexGuard<State> {
50-
self.0.lock().unwrap_or_else(|e| e.into_inner())
97+
fn parse_from_meta<T>(
98+
meta: &Vec<NestedMeta>,
99+
call_site: Option<Span>,
100+
) -> Result<T, TokenStream2>
101+
where
102+
T: FromMeta,
103+
{
104+
T::from_list(&meta).map_err(|e| {
105+
syn::Error::new(
106+
call_site.unwrap_or_else(Span::call_site),
107+
format!("Unable to parse attribute arguments: {:?}", e),
108+
)
109+
.to_compile_error()
110+
.into()
111+
})
51112
}
52113
}
53114

@@ -67,8 +128,12 @@ pub fn php_class(args: TokenStream, input: TokenStream) -> TokenStream {
67128
pub fn php_function(args: TokenStream, input: TokenStream) -> TokenStream {
68129
let args = parse_macro_input!(args as AttributeArgs);
69130
let input = parse_macro_input!(input as ItemFn);
131+
let attr_args = match State::parse_from_meta(&args, None) {
132+
Ok(attr_args) => attr_args,
133+
Err(e) => return e.into(),
134+
};
70135

71-
match function::parser(args, input) {
136+
match function::parser(attr_args, &input) {
72137
Ok((parsed, _)) => parsed,
73138
Err(e) => syn::Error::new(Span::call_site(), e).to_compile_error(),
74139
}
@@ -77,7 +142,7 @@ pub fn php_function(args: TokenStream, input: TokenStream) -> TokenStream {
77142

78143
#[proc_macro_attribute]
79144
pub fn php_module(_: TokenStream, input: TokenStream) -> TokenStream {
80-
let input = parse_macro_input!(input as ItemFn);
145+
let input = parse_macro_input!(input as ItemMod);
81146

82147
match module::parser(input) {
83148
Ok(parsed) => parsed,
@@ -90,9 +155,13 @@ pub fn php_module(_: TokenStream, input: TokenStream) -> TokenStream {
90155
pub fn php_startup(args: TokenStream, input: TokenStream) -> TokenStream {
91156
let args = parse_macro_input!(args as AttributeArgs);
92157
let input = parse_macro_input!(input as ItemFn);
158+
let attr_args = match State::parse_from_meta(&args, None) {
159+
Ok(attr_args) => attr_args,
160+
Err(e) => return e.into(),
161+
};
93162

94-
match startup_function::parser(Some(args), input) {
95-
Ok(parsed) => parsed,
163+
match startup_function::parser(Some(attr_args), &input) {
164+
Ok((parsed, _)) => parsed,
96165
Err(e) => syn::Error::new(Span::call_site(), e).to_compile_error(),
97166
}
98167
.into()

0 commit comments

Comments
 (0)