feat!: allow field attibutes (#121)

This commit is contained in:
Mike Voronov 2023-07-24 16:48:11 +03:00 committed by GitHub
parent 7479c36413
commit 7a39cd35ad
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 50 additions and 19 deletions

View File

@ -34,7 +34,7 @@ impl ParseMacroInput for syn::ItemStruct {
_ => return syn_error!(self.span(), "only named fields are allowed in structs"), _ => return syn_error!(self.span(), "only named fields are allowed in structs"),
}; };
let fields = fields_into_ast(fields)?; let fields = fields_into_ast(fields, &self.ident)?;
let fields = AstRecordFields::Named(fields); let fields = AstRecordFields::Named(fields);
let name = self.ident.to_string(); let name = self.ident.to_string();
@ -64,11 +64,13 @@ fn check_record(record: &syn::ItemStruct) -> Result<()> {
fn fields_into_ast( fn fields_into_ast(
fields: &syn::punctuated::Punctuated<syn::Field, syn::Token![,]>, fields: &syn::punctuated::Punctuated<syn::Field, syn::Token![,]>,
record_ident: &syn::Ident,
) -> Result<Vec<AstRecordField>> { ) -> Result<Vec<AstRecordField>> {
fields fields
.iter() .iter()
.map(|field| { .map(|field| {
check_field(field)?; maybe_warn_about_non_doc_attributes(field, record_ident);
let name = field.ident.as_ref().map(|ident| { let name = field.ident.as_ref().map(|ident| {
ident ident
.to_string() .to_string()
@ -85,23 +87,31 @@ fn fields_into_ast(
.collect::<Result<Vec<_>>>() .collect::<Result<Vec<_>>>()
} }
/// Check that record fields satisfy the following requirements: /// Prints an error if a field has an any attribute except doc.
/// - field must have only doc attributes fn maybe_warn_about_non_doc_attributes(field: &syn::Field, record_ident: &syn::Ident) {
fn check_field(field: &syn::Field) -> Result<()> { for attr in field.attrs.iter() {
match attr.parse_meta() {
Ok(meta) if is_doc_attribute(&meta) => continue,
_ => {}
}
// TODO: print message with a span when diagnostic API stabilized
// https://github.com/rust-lang/rust/issues/54140
match &field.ident {
Some(ident) => eprintln!(
r#"warning: field "{}" of struct "{}" has an attribute which could cause compatibility issues"#,
ident, record_ident
),
None => eprintln!(
r#"warning: field of struct "{}" has an attribute which could cause compatibility issues"#,
record_ident
),
};
}
}
fn is_doc_attribute(meta: &syn::Meta) -> bool {
const DOC_ATTR_NAME: &str = "doc"; const DOC_ATTR_NAME: &str = "doc";
// Check that all attributes are doc attributes meta.path().is_ident(DOC_ATTR_NAME)
let are_all_attrs_doc = field.attrs.iter().all(|attr| {
let meta = match attr.parse_meta() {
Ok(meta) => meta,
Err(_) => return false,
};
meta.path().is_ident(DOC_ATTR_NAME)
});
if !are_all_attrs_doc {
return syn_error!(field.span(), "field attributes isn't allowed");
}
Ok(())
} }

View File

@ -0,0 +1,9 @@
use marine_rs_sdk::marine;
fn main() {}
#[marine]
struct A {
#[cfg(target_os = "wasm")]
pub field: i64,
}

View File

@ -0,0 +1 @@
warning: field "field" of struct "A" has an attribute which could cause compatibility issues

View File

@ -0,0 +1,9 @@
use marine_rs_sdk::marine;
fn main() {}
#[marine]
struct A {
#[doc = "This is a doc attribute"]
pub field: i64,
}

View File

@ -19,6 +19,8 @@ fn marine_compilation_tests() {
tests.pass("tests/compilation_tests/records/basic_structs.rs"); tests.pass("tests/compilation_tests/records/basic_structs.rs");
tests.pass("tests/compilation_tests/records/empty_struct.rs"); tests.pass("tests/compilation_tests/records/empty_struct.rs");
tests.pass("tests/compilation_tests/records/struct_with_attribute.rs");
tests.pass("tests/compilation_tests/records/struct_with_doc_attribute.rs");
tests.pass("tests/compilation_tests/records/struct_with_private_fields.rs"); tests.pass("tests/compilation_tests/records/struct_with_private_fields.rs");
tests.compile_fail("tests/compilation_tests/records/struct_with_improper_types.rs"); tests.compile_fail("tests/compilation_tests/records/struct_with_improper_types.rs");
tests.compile_fail("tests/compilation_tests/records/unnamed_structs.rs"); tests.compile_fail("tests/compilation_tests/records/unnamed_structs.rs");