Skip to content

Commit

Permalink
macros: Clean up Context use and ensure we don't miss errors
Browse files Browse the repository at this point in the history
  • Loading branch information
udoprog committed Oct 30, 2024
1 parent a21078f commit da30012
Show file tree
Hide file tree
Showing 10 changed files with 175 additions and 219 deletions.
39 changes: 23 additions & 16 deletions crates/rune-macros/src/any.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,14 +84,22 @@ impl syn::parse::Parse for Derive {

impl Derive {
pub(super) fn into_any_builder(self, cx: &Context) -> Result<TypeBuilder<syn::Ident>, ()> {
let attr = cx.type_attrs(&self.input.attrs)?;

let attr = cx.type_attrs(&self.input.attrs);
let tokens = cx.tokens_with_module(attr.module.as_ref());

let mut installers = Vec::new();

expand_install_with(cx, &self.input, &tokens, &attr, &mut installers)?;

if matches!(&self.input.data, syn::Data::Enum(..)) {
if let Some(span) = attr.constructor {
cx.error(syn::Error::new(
span,
"#[rune(constructor)] is not supported on enums, only its variants",
));
}
}

let name = match &attr.name {
Some(name) => name,
None => &self.input.ident,
Expand Down Expand Up @@ -174,7 +182,7 @@ fn expand_struct_install_with(
attr: &TypeAttr,
) -> Result<(), ()> {
for (n, field) in st.fields.iter().enumerate() {
let attrs = cx.field_attrs(&field.attrs)?;
let attrs = cx.field_attrs(&field.attrs);
let name;
let index;

Expand Down Expand Up @@ -224,6 +232,7 @@ fn expand_struct_install_with(
syn::Fields::Named(fields) => {
let constructor = attr
.constructor
.is_some()
.then(|| {
let args = fields.named.iter().map(|f| {
let ident = f.ident.as_ref().expect("named fields must have an Ident");
Expand Down Expand Up @@ -301,7 +310,7 @@ fn expand_enum_install_with(
for (variant_index, variant) in en.variants.iter().enumerate() {
let span = variant.fields.span();

let variant_attr = cx.variant_attr(&variant.attrs)?;
let variant_attr = cx.variant_attr(&variant.attrs);

let mut variant_docs = syn::ExprArray {
attrs: Vec::new(),
Expand All @@ -323,7 +332,7 @@ fn expand_enum_install_with(
let mut field_names = Vec::new();

for f in &fields.named {
let attrs = cx.field_attrs(&f.attrs)?;
let attrs = cx.field_attrs(&f.attrs);

let Some(f_ident) = &f.ident else {
cx.error(syn::Error::new_spanned(f, "Missing field name"));
Expand Down Expand Up @@ -358,7 +367,7 @@ fn expand_enum_install_with(

for (n, field) in fields.unnamed.iter().enumerate() {
let span = field.span();
let attrs = cx.field_attrs(&field.attrs)?;
let attrs = cx.field_attrs(&field.attrs);

if attrs.field {
fields_len += 1;
Expand All @@ -379,16 +388,14 @@ fn expand_enum_install_with(
enum_.variant_mut(#variant_index)?.make_unnamed(#fields_len)?.static_docs(&#variant_docs)?
});

let constructor = if variant_attr.constructor {
if fields_len != fields.unnamed.len() {
cx.error(syn::Error::new_spanned(fields, "#[rune(constructor)] can only be used if all fields are marked with #[rune(get)"));
return Err(());
}
if variant_attr.constructor.is_some() && fields_len != fields.unnamed.len() {
cx.error(syn::Error::new_spanned(fields, "#[rune(constructor)] can only be used if all fields are marked with #[rune(get)"));
}

Some(quote!(#ident #type_generics :: #variant_ident))
} else {
None
};
let constructor = variant_attr
.constructor
.is_some()
.then(|| quote!(#ident #type_generics :: #variant_ident));

variants.push((constructor, variant_attr));
}
Expand All @@ -397,7 +404,7 @@ fn expand_enum_install_with(
enum_.variant_mut(#variant_index)?.make_empty()?.static_docs(&#variant_docs)?
});

let constructor = if variant_attr.constructor {
let constructor = if variant_attr.constructor.is_some() {
Some(quote!(|| #ident #type_generics :: #variant_ident))
} else {
None
Expand Down
4 changes: 2 additions & 2 deletions crates/rune-macros/src/const_value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ pub(super) struct ConstBuilder<T> {

impl Derive {
pub(super) fn into_builder(self, cx: &Context) -> Result<ConstBuilder<syn::Ident>, ()> {
let attr = cx.const_value_type_attrs(&self.input.attrs)?;
let attr = cx.const_value_type_attrs(&self.input.attrs);
let tokens = cx.tokens_with_module(attr.module.as_ref());
let body;

Expand All @@ -57,7 +57,7 @@ impl Derive {
let mut fields = Vec::new();

for (index, field) in data.fields.iter().enumerate() {
let attr = cx.const_value_field_attrs(&field.attrs)?;
let attr = cx.const_value_field_attrs(&field.attrs);

let member = match &field.ident {
Some(ident) => syn::Member::Named(ident.clone()),
Expand Down
123 changes: 70 additions & 53 deletions crates/rune-macros/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use syn::Token;

/// Parsed `#[rune(..)]` field attributes.
#[derive(Default)]
#[must_use = "Attributes must be used or explicitly ignored"]
pub(crate) struct FieldAttrs {
/// A field that is an identifier. Should use `Default::default` to be
/// constructed and ignored during `ToTokens` and `Spanned`.
Expand Down Expand Up @@ -46,6 +47,7 @@ impl FieldAttrs {

/// Parsed #[const_value(..)] field attributes.
#[derive(Default)]
#[must_use = "Attributes must be used or explicitly ignored"]
pub(crate) struct ConstValueFieldAttrs {
/// Define a custom parsing method.
pub(crate) with: Option<syn::Path>,
Expand All @@ -68,6 +70,7 @@ impl Default for ParseKind {

/// Parsed field attributes.
#[derive(Default)]
#[must_use = "Attributes must be used or explicitly ignored"]
pub(crate) struct TypeAttr {
/// `#[rune(name = TypeName)]` to override the default type name.
pub(crate) name: Option<syn::Ident>,
Expand All @@ -80,7 +83,7 @@ pub(crate) struct TypeAttr {
/// `#[rune(item = <path>)]`.
pub(crate) item: Option<syn::Path>,
/// `#[rune(constructor)]`.
pub(crate) constructor: bool,
pub(crate) constructor: Option<Span>,
/// Parsed documentation.
pub(crate) docs: Vec<syn::Expr>,
/// Indicates that this is a builtin type, so don't generate an `Any`
Expand All @@ -100,16 +103,18 @@ pub(crate) struct TypeAttr {

/// Parsed #[const_value(..)] field attributes.
#[derive(Default)]
#[must_use = "Attributes must be used or explicitly ignored"]
pub(crate) struct ConstValueTypeAttr {
/// `#[const_value(module = <path>)]`.
pub(crate) module: Option<syn::Path>,
}

/// Parsed variant attributes.
#[derive(Default)]
#[must_use = "Attributes must be used or explicitly ignored"]
pub(crate) struct VariantAttrs {
/// `#[rune(constructor)]`.
pub(crate) constructor: bool,
pub(crate) constructor: Option<Span>,
/// Discovered documentation.
pub(crate) docs: Vec<syn::Expr>,
}
Expand Down Expand Up @@ -169,6 +174,47 @@ impl Context {
}
}

/// Helper to build using a function that takes a context.
pub(super) fn build(f: impl FnOnce(&Self) -> Result<TokenStream, ()>) -> TokenStream {
let cx = Self::new();
cx.build_inner(f)
}

/// Helper to build using a function that takes a context internally.
pub(super) fn build_with_crate(
f: impl FnOnce(&Self) -> Result<TokenStream, ()>,
) -> TokenStream {
let cx = Self::with_crate();
cx.build_inner(f)
}

fn build_inner(self, f: impl FnOnce(&Self) -> Result<TokenStream, ()>) -> TokenStream {
fn to_compile_errors<I>(errors: I) -> TokenStream
where
I: IntoIterator<Item = syn::Error>,
{
let mut stream = TokenStream::default();

for error in errors {
stream.extend(error.into_compile_error());
}

stream
}

let Ok(builder) = f(&self) else {
return to_compile_errors(self.errors.into_inner());
};

let errors = self.errors.into_inner();

if !errors.is_empty() {
return to_compile_errors(errors);
}

builder
}

/// Register an error.
pub(crate) fn error(&self, error: syn::Error) {
self.errors.borrow_mut().push(error)
Expand All @@ -192,11 +238,7 @@ impl Context {
Ok(ident)
}

pub(crate) fn const_value_field_attrs(
&self,
input: &[syn::Attribute],
) -> Result<ConstValueFieldAttrs, ()> {
let mut error = false;
pub(crate) fn const_value_field_attrs(&self, input: &[syn::Attribute]) -> ConstValueFieldAttrs {
let mut attr = ConstValueFieldAttrs::default();

for a in input {
Expand All @@ -218,20 +260,15 @@ impl Context {
});

if let Err(e) = result {
error = true;
self.error(e);
};
}

if error {
return Err(());
}

Ok(attr)
attr
}

/// Parse field attributes.
pub(crate) fn field_attrs(&self, input: &[syn::Attribute]) -> Result<FieldAttrs, ()> {
pub(crate) fn field_attrs(&self, input: &[syn::Attribute]) -> FieldAttrs {
macro_rules! generate_assign {
($proto:ident, $op:tt) => {
|g| {
Expand Down Expand Up @@ -277,7 +314,6 @@ impl Context {
};
}

let mut error = false;
let mut attr = FieldAttrs::default();

for a in input {
Expand Down Expand Up @@ -457,23 +493,14 @@ impl Context {
});

if let Err(e) = result {
error = true;
self.error(e);
}
}

if error {
return Err(());
}

Ok(attr)
attr
}

pub(crate) fn const_value_type_attrs(
&self,
input: &[syn::Attribute],
) -> Result<ConstValueTypeAttr, ()> {
let mut error = false;
pub(crate) fn const_value_type_attrs(&self, input: &[syn::Attribute]) -> ConstValueTypeAttr {
let mut attr = ConstValueTypeAttr::default();

for a in input {
Expand All @@ -500,21 +527,15 @@ impl Context {
});

if let Err(e) = result {
error = true;
self.error(e);
};
}

if error {
return Err(());
}

Ok(attr)
attr
}

/// Parse field attributes.
pub(crate) fn type_attrs(&self, input: &[syn::Attribute]) -> Result<TypeAttr, ()> {
let mut error = false;
pub(crate) fn type_attrs(&self, input: &[syn::Attribute]) -> TypeAttr {
let mut attr = TypeAttr::default();

for a in input {
Expand Down Expand Up @@ -567,7 +588,14 @@ impl Context {
meta.input.parse::<Token![=]>()?;
attr.install_with = Some(parse_path_compat(meta.input)?);
} else if meta.path == CONSTRUCTOR {
attr.constructor = true;
if attr.constructor.is_some() {
return Err(syn::Error::new(
meta.path.span(),
"#[rune(constructor)] must only be used once",
));
}

attr.constructor = Some(meta.path.span());
} else if meta.path == BUILTIN {
attr.builtin = Some(meta.path.span());
} else if meta.path == STATIC_TYPE {
Expand Down Expand Up @@ -599,23 +627,17 @@ impl Context {
});

if let Err(e) = result {
error = true;
self.error(e);
};
}
}

if error {
return Err(());
}

Ok(attr)
attr
}

/// Parse and extract variant attributes.
pub(crate) fn variant_attr(&self, input: &[syn::Attribute]) -> Result<VariantAttrs, ()> {
pub(crate) fn variant_attr(&self, input: &[syn::Attribute]) -> VariantAttrs {
let mut attr = VariantAttrs::default();
let mut error = false;

for a in input {
if a.path().is_ident("doc") {
Expand All @@ -629,14 +651,14 @@ impl Context {
if a.path() == RUNE {
let result = a.parse_nested_meta(|meta| {
if meta.path == CONSTRUCTOR {
if attr.constructor {
return Err(syn::Error::new_spanned(
&meta.path,
if attr.constructor.is_some() {
return Err(syn::Error::new(
meta.path.span(),
"#[rune(constructor)] must only be used once",
));
}

attr.constructor = true;
attr.constructor = Some(meta.path.span());
} else {
return Err(syn::Error::new_spanned(&meta.path, "Unsupported attribute"));
}
Expand All @@ -645,17 +667,12 @@ impl Context {
});

if let Err(e) = result {
error = true;
self.error(e);
};
}
}

if error {
return Err(());
}

Ok(attr)
attr
}

/// Parse path to custom field function.
Expand Down
Loading

0 comments on commit da30012

Please sign in to comment.