From 435c721535209f4b37922c255099046c3a72587e Mon Sep 17 00:00:00 2001 From: Nikita Strygin Date: Mon, 18 Sep 2023 17:11:21 +0300 Subject: [PATCH] [refactor] #3882: Add tests for derive(HasOrigin) macro, fix error reporting on stable Signed-off-by: Nikita Strygin --- data_model/derive/tests/has_origin.rs | 53 +++++++++++++++++++ .../ui_fail/has_origin_multiple_attributes.rs | 9 ++++ .../has_origin_multiple_attributes.stderr | 6 +++ macro/utils/src/lib.rs | 51 +++++++++++++++--- 4 files changed, 112 insertions(+), 7 deletions(-) create mode 100644 data_model/derive/tests/has_origin.rs create mode 100644 data_model/derive/tests/ui_fail/has_origin_multiple_attributes.rs create mode 100644 data_model/derive/tests/ui_fail/has_origin_multiple_attributes.stderr diff --git a/data_model/derive/tests/has_origin.rs b/data_model/derive/tests/has_origin.rs new file mode 100644 index 00000000000..8522c4268fd --- /dev/null +++ b/data_model/derive/tests/has_origin.rs @@ -0,0 +1,53 @@ +use iroha_data_model::prelude::{HasOrigin, Identifiable}; +use iroha_data_model_derive::{HasOrigin, IdEqOrdHash}; + +#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Hash)] +struct ObjectId(pub i32); + +// fake impl for `#[derive(IdEqOrdHash)]` +impl From for iroha_data_model::IdBox { + fn from(_: ObjectId) -> Self { + unimplemented!("fake impl") + } +} + +#[derive(Debug, IdEqOrdHash)] +struct Object { + id: ObjectId, +} + +impl Object { + fn id(&self) -> &ObjectId { + &self.id + } +} + +#[allow(clippy::enum_variant_names)] // it's a test, duh +#[derive(Debug, HasOrigin)] +#[has_origin(origin = Object)] +enum ObjectEvent { + EventWithId(ObjectId), + #[has_origin(event => &event.0)] + EventWithExtractor((ObjectId, i32)), + #[has_origin(obj => obj.id())] + EventWithAnotherExtractor(Object), +} + +#[test] +fn has_origin() { + let events = vec![ + ObjectEvent::EventWithId(ObjectId(1)), + ObjectEvent::EventWithExtractor((ObjectId(2), 2)), + ObjectEvent::EventWithAnotherExtractor(Object { id: ObjectId(3) }), + ]; + let expected_ids = vec![ObjectId(1), ObjectId(2), ObjectId(3)]; + + for (event, expected_id) in events.into_iter().zip(expected_ids) { + assert_eq!( + event.origin_id(), + &expected_id, + "mismatched origin id for event {:?}", + event + ); + } +} diff --git a/data_model/derive/tests/ui_fail/has_origin_multiple_attributes.rs b/data_model/derive/tests/ui_fail/has_origin_multiple_attributes.rs new file mode 100644 index 00000000000..ad09416af20 --- /dev/null +++ b/data_model/derive/tests/ui_fail/has_origin_multiple_attributes.rs @@ -0,0 +1,9 @@ +use iroha_data_model_derive::HasOrigin; + +#[derive(HasOrigin)] +#[has_origin(origin = Object)] +#[has_origin(origin = Object)] +#[has_origin(origin = Object)] +enum MultipleAttributes {} + +fn main() {} diff --git a/data_model/derive/tests/ui_fail/has_origin_multiple_attributes.stderr b/data_model/derive/tests/ui_fail/has_origin_multiple_attributes.stderr new file mode 100644 index 00000000000..35511493350 --- /dev/null +++ b/data_model/derive/tests/ui_fail/has_origin_multiple_attributes.stderr @@ -0,0 +1,6 @@ +error: Only one #[has_origin] attribute is allowed! + --> tests/ui_fail/has_origin_multiple_attributes.rs:5:1 + | +5 | / #[has_origin(origin = Object)] +6 | | #[has_origin(origin = Object)] + | |______________________________^ diff --git a/macro/utils/src/lib.rs b/macro/utils/src/lib.rs index 85f1bdd7e65..1177aae5629 100644 --- a/macro/utils/src/lib.rs +++ b/macro/utils/src/lib.rs @@ -70,9 +70,46 @@ macro_rules! attr_struct { }; } +/// Extension trait for [`darling::Error`]. +/// +/// Currently exists to add `with_spans` method. +pub trait DarlingErrorExt: Sized { + /// Attaches a combination of multiple spans to the error. + /// + /// Note that it only attaches the first span on stable rustc, as the `Span::join` method is not yet stabilized (https://github.com/rust-lang/rust/issues/54725#issuecomment-649078500). + fn with_spans(self, spans: impl IntoIterator>) -> Self; +} + +impl DarlingErrorExt for darling::Error { + fn with_spans(self, spans: impl IntoIterator>) -> Self { + // Unfortunately, the story for combining multiple spans in rustc proc macro is not yet complete. + // (see https://github.com/rust-lang/rust/issues/54725#issuecomment-649078500, https://github.com/rust-lang/rust/issues/54725#issuecomment-1547795742) + // syn does some hacks to get error reporting that is a bit better: https://docs.rs/syn/2.0.37/src/syn/error.rs.html#282 + // we can't to that because darling's error type does not let us do that. + + // on nightly, we are fine, as `.join` method works. On stable, we fall back to returning the first span. + + let mut iter = spans.into_iter(); + let Some(first) = iter.next() else { + return self; + }; + let first: proc_macro2::Span = first.into(); + let r = iter + .try_fold(first, |a, b| a.join(b.into())) + .unwrap_or(first); + + self.with_span(&r) + } +} + /// Parses a single attribute of the form `#[attr_name(...)]` for darling using a `syn::parse::Parse` implementation. /// /// If no attribute with specified name is found, returns `Ok(None)`. +/// +/// # Errors +/// +/// - If multiple attributes with specified name are found +/// - If attribute is not a list pub fn parse_single_list_attr_opt( attr_name: &str, attrs: &[syn2::Attribute], @@ -93,13 +130,7 @@ pub fn parse_single_list_attr_opt( // allow parsing to proceed further to collect more errors accumulator.push( darling::Error::custom(format!("Only one #[{}] attribute is allowed!", attr_name)) - .with_span( - &tail - .iter() - .map(syn2::spanned::Spanned::span) - .reduce(|a, b| a.join(b).unwrap()) - .unwrap(), - ), + .with_spans(tail.iter().map(syn2::spanned::Spanned::span)), ); attr } @@ -122,6 +153,12 @@ pub fn parse_single_list_attr_opt( /// Parses a single attribute of the form `#[attr_name(...)]` for darling using a `syn::parse::Parse` implementation. /// /// If no attribute with specified name is found, returns an error. +/// +/// # Errors +/// +/// - If multiple attributes with specified name are found +/// - If attribute is not a list +/// - If attribute is not found pub fn parse_single_list_attr( attr_name: &str, attrs: &[syn2::Attribute],