Skip to content

Commit cfecbd5

Browse files
JakobDegenfacebook-github-bot
authored andcommitted
module: Reuse the user's ItemFn
Summary: Instead of constructing our own `ItemFn`, mostly just reuse the one provided by the user, with some adjustments. The next step in this stack is to add generics support to this stuff, and this makes it much easier to preserve the generics Reviewed By: ndmitchell Differential Revision: D73726013 fbshipit-source-id: f4e4687da0831cf8404543222913b23f7638f7a0
1 parent dde7ce9 commit cfecbd5

File tree

3 files changed

+64
-49
lines changed

3 files changed

+64
-49
lines changed

starlark_derive/src/module/parse.rs

Lines changed: 40 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -56,26 +56,23 @@ impl ModuleKind {
5656
}
5757

5858
pub(crate) fn parse(mut input: ItemFn) -> syn::Result<StarModule> {
59-
let (module_docstring, attrs) = parse_module_attributes(&input)?;
60-
let visibility = input.vis;
61-
let sig_span = input.sig.span();
62-
let name = input.sig.ident;
59+
let module_docstring = parse_module_docstring(&input)?;
6360

6461
if input.sig.inputs.len() != 1 {
6562
return Err(syn::Error::new(
66-
sig_span,
63+
input.sig.span(),
6764
"function must have exactly one argument",
6865
));
6966
}
70-
let arg = input.sig.inputs.pop().unwrap();
67+
let arg = input.sig.inputs.last_mut().unwrap();
7168
let arg_span = arg.span();
7269

73-
let (ty, module_kind) = match arg.into_value() {
74-
FnArg::Typed(PatType { ty, .. }) if is_mut_globals_builder(&ty) => {
75-
(ty, ModuleKind::Globals)
70+
let (pat, module_kind) = match arg {
71+
FnArg::Typed(PatType { ty, pat, .. }) if is_mut_globals_builder(&ty) => {
72+
(pat, ModuleKind::Globals)
7673
}
77-
FnArg::Typed(PatType { ty, .. }) if is_mut_methods_builder(&ty) => {
78-
(ty, ModuleKind::Methods)
74+
FnArg::Typed(PatType { ty, pat, .. }) if is_mut_methods_builder(&ty) => {
75+
(pat, ModuleKind::Methods)
7976
}
8077
_ => {
8178
return Err(syn::Error::new(
@@ -84,19 +81,36 @@ pub(crate) fn parse(mut input: ItemFn) -> syn::Result<StarModule> {
8481
));
8582
}
8683
};
84+
// FIXME(JakobDegen): Pick one form and enforce it
85+
let (syn::Pat::Ident(_) | syn::Pat::Wild(_)) = &mut **pat else {
86+
return Err(syn::Error::new(pat.span(), "Expected ident"));
87+
};
88+
// Replace the argument with a known one - the user can't depend on it anyway
89+
*pat = Box::new(syn::Pat::Ident(syn::PatIdent {
90+
attrs: Default::default(),
91+
by_ref: None,
92+
mutability: None,
93+
ident: syn::Ident::new("globals_builder", pat.span()),
94+
subpat: None,
95+
}));
96+
97+
let stmts = std::mem::replace(
98+
&mut input.block,
99+
Box::new(syn::Block {
100+
brace_token: Default::default(),
101+
stmts: Vec::new(),
102+
}),
103+
)
104+
.stmts
105+
.into_iter()
106+
.map(|stmt| parse_stmt(stmt, module_kind))
107+
.collect::<syn::Result<_>>()?;
108+
87109
Ok(StarModule {
88110
module_kind,
89-
visibility,
90-
globals_builder: *ty,
91-
name,
92-
attrs,
111+
input,
93112
docstring: module_docstring,
94-
stmts: input
95-
.block
96-
.stmts
97-
.into_iter()
98-
.map(|stmt| parse_stmt(stmt, module_kind))
99-
.collect::<syn::Result<_>>()?,
113+
stmts,
100114
})
101115
}
102116

@@ -127,22 +141,18 @@ fn is_attribute_docstring(x: &Attribute) -> syn::Result<Option<String>> {
127141
}
128142

129143
/// Return (docstring, other attributes)
130-
fn parse_module_attributes(input: &ItemFn) -> syn::Result<(Option<String>, Vec<Attribute>)> {
144+
fn parse_module_docstring(input: &ItemFn) -> syn::Result<Option<String>> {
131145
let mut doc_attrs = Vec::new();
132-
let mut attrs = Vec::new();
133146
for attr in &input.attrs {
134147
if let Some(ds) = is_attribute_docstring(attr)? {
135148
doc_attrs.push(ds);
136-
} else {
137-
attrs.push(attr.clone());
138149
}
139150
}
140-
let docs = if doc_attrs.is_empty() {
141-
None
151+
if doc_attrs.is_empty() {
152+
Ok(None)
142153
} else {
143-
Some(doc_attrs.join("\n"))
144-
};
145-
Ok((docs, attrs))
154+
Ok(Some(doc_attrs.join("\n")))
155+
}
146156
}
147157

148158
fn parse_stmt(stmt: Stmt, module_kind: ModuleKind) -> syn::Result<StarStmt> {

starlark_derive/src/module/render.rs

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,7 @@ pub(crate) fn render(x: StarModule) -> syn::Result<TokenStream> {
4242

4343
fn render_impl(x: StarModule) -> syn::Result<syn::ItemFn> {
4444
let StarModule {
45-
name,
46-
globals_builder,
47-
visibility,
48-
attrs,
45+
mut input,
4946
docstring,
5047
stmts,
5148
module_kind,
@@ -56,19 +53,31 @@ fn render_impl(x: StarModule) -> syn::Result<syn::ItemFn> {
5653
.map(render_stmt)
5754
.collect::<syn::Result<_>>()?;
5855
let set_docstring = docstring.map(|ds| quote!(globals_builder.set_docstring(#ds);));
59-
Ok(syn::parse_quote! {
60-
#( #attrs )*
61-
#visibility fn #name(globals_builder: #globals_builder) {
62-
fn build(globals_builder: #globals_builder) {
56+
57+
let inner_fn = syn::ItemFn {
58+
attrs: Default::default(),
59+
vis: syn::Visibility::Inherited,
60+
sig: syn::Signature {
61+
ident: syn::Ident::new("build", input.sig.ident.span()),
62+
..input.sig.clone()
63+
},
64+
block: syn::parse_quote! {
65+
{
6366
#set_docstring
6467
#( #stmts )*
6568
// Mute warning if stmts is empty.
6669
let _ = globals_builder;
6770
}
71+
},
72+
};
73+
input.block = syn::parse_quote! {
74+
{
75+
#inner_fn
6876
static RES: starlark::environment::#statics = starlark::environment::#statics::new();
6977
RES.populate(build, globals_builder);
7078
}
71-
})
79+
};
80+
Ok(input)
7281
}
7382

7483
fn render_stmt(x: StarStmt) -> syn::Result<syn::Stmt> {

starlark_derive/src/module/typ.rs

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ use syn::Attribute;
2222
use syn::Block;
2323
use syn::Expr;
2424
use syn::Type;
25-
use syn::Visibility;
2625
use syn::spanned::Spanned;
2726

2827
use crate::module::parse::ModuleKind;
@@ -32,15 +31,12 @@ use crate::module::util::unpack_option;
3231

3332
#[derive(Debug)]
3433
pub(crate) struct StarModule {
35-
pub module_kind: ModuleKind,
36-
pub visibility: Visibility,
37-
// We reuse the users globals_builder to make sure `use` statements etc
38-
// make sense
39-
pub globals_builder: Type,
40-
pub name: Ident,
41-
pub attrs: Vec<Attribute>,
42-
pub docstring: Option<String>,
43-
pub stmts: Vec<StarStmt>,
34+
pub(crate) module_kind: ModuleKind,
35+
/// The input `ItemFn`, with the body replaced by an empty block, and the one parameter having
36+
/// been renamed to `globals_builder`
37+
pub(crate) input: syn::ItemFn,
38+
pub(crate) docstring: Option<String>,
39+
pub(crate) stmts: Vec<StarStmt>,
4440
}
4541

4642
#[allow(clippy::large_enum_variant)]

0 commit comments

Comments
 (0)