diff --git a/crates/marine-macro-impl/src/parse_macro_input/item_record.rs b/crates/marine-macro-impl/src/parse_macro_input/item_record.rs index 0b6cbea..0cabf34 100644 --- a/crates/marine-macro-impl/src/parse_macro_input/item_record.rs +++ b/crates/marine-macro-impl/src/parse_macro_input/item_record.rs @@ -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, + record_ident: &syn::Ident, ) -> Result> { 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::>>() } -/// 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) } diff --git a/tests/compilation_tests/records/struct_with_attribute.rs b/tests/compilation_tests/records/struct_with_attribute.rs new file mode 100644 index 0000000..4084d3a --- /dev/null +++ b/tests/compilation_tests/records/struct_with_attribute.rs @@ -0,0 +1,9 @@ +use marine_rs_sdk::marine; + +fn main() {} + +#[marine] +struct A { + #[cfg(target_os = "wasm")] + pub field: i64, +} diff --git a/tests/compilation_tests/records/struct_with_attributes.stderr b/tests/compilation_tests/records/struct_with_attributes.stderr new file mode 100644 index 0000000..7463e37 --- /dev/null +++ b/tests/compilation_tests/records/struct_with_attributes.stderr @@ -0,0 +1 @@ +warning: field "field" of struct "A" has an attribute which could cause compatibility issues diff --git a/tests/compilation_tests/records/struct_with_doc_attribute.rs b/tests/compilation_tests/records/struct_with_doc_attribute.rs new file mode 100644 index 0000000..6f7c5ae --- /dev/null +++ b/tests/compilation_tests/records/struct_with_doc_attribute.rs @@ -0,0 +1,9 @@ +use marine_rs_sdk::marine; + +fn main() {} + +#[marine] +struct A { + #[doc = "This is a doc attribute"] + pub field: i64, +} diff --git a/tests/compilation_tests_runner.rs b/tests/compilation_tests_runner.rs index a420160..7e3ffae 100644 --- a/tests/compilation_tests_runner.rs +++ b/tests/compilation_tests_runner.rs @@ -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");