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"),
};
let fields = fields_into_ast(fields)?;
let fields = fields_into_ast(fields, &self.ident)?;
let fields = AstRecordFields::Named(fields);
let name = self.ident.to_string();
@ -64,11 +64,13 @@ fn check_record(record: &syn::ItemStruct) -> Result<()> {
fn fields_into_ast(
fields: &syn::punctuated::Punctuated<syn::Field, syn::Token![,]>,
record_ident: &syn::Ident,
) -> Result<Vec<AstRecordField>> {
fields
.iter()
.map(|field| {
check_field(field)?;
maybe_warn_about_non_doc_attributes(field, record_ident);
let name = field.ident.as_ref().map(|ident| {
ident
.to_string()
@ -85,23 +87,31 @@ fn fields_into_ast(
.collect::<Result<Vec<_>>>()
}
/// Check that record fields satisfy the following requirements:
/// - field must have only doc attributes
fn check_field(field: &syn::Field) -> Result<()> {
/// Prints an error if a field has an any attribute except doc.
fn maybe_warn_about_non_doc_attributes(field: &syn::Field, record_ident: &syn::Ident) {
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";
// Check that all attributes are doc attributes
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(())
meta.path().is_ident(DOC_ATTR_NAME)
}

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/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.compile_fail("tests/compilation_tests/records/struct_with_improper_types.rs");
tests.compile_fail("tests/compilation_tests/records/unnamed_structs.rs");