From 3e8088a6697cccfd1cd5082f6df7c6004dcbd54f Mon Sep 17 00:00:00 2001 From: Arne Beer Date: Sat, 2 Apr 2022 15:11:49 +0100 Subject: [PATCH] change: compile error if different tokens --- codegen/src/generate/into/normal.rs | 27 +++--- codegen/src/generate/into/with_default.rs | 18 ++-- codegen/src/generate/merge/borrowed.rs | 38 +++++--- codegen/src/generate/merge/owned.rs | 99 +++++++++++++------- testing/tests/merge/incompatible_type.rs | 4 +- testing/tests/merge/incompatible_type.stderr | 22 +++-- testing/tests/stub_declarations/lib.rs | 1 + 7 files changed, 135 insertions(+), 74 deletions(-) diff --git a/codegen/src/generate/into/normal.rs b/codegen/src/generate/into/normal.rs index c2f51db..2425d85 100644 --- a/codegen/src/generate/into/normal.rs +++ b/codegen/src/generate/into/normal.rs @@ -31,9 +31,9 @@ pub(crate) fn impl_into(params: &Parameters, fields: Vec<(Field, Field)>) -> Tok fn into(params: &Parameters, fields: Vec<(Field, Field)>) -> TokenStream { let mut assignments = TokenStream::new(); - for (src_field, dest_field) in fields { + for (src_field, target_field) in fields { let src_field_ident = src_field.ident; - let dest_field_ident = dest_field.ident; + let target_field_ident = target_field.ident; // Find out, whether the fields are optional or not. let src_field_type = match determine_field_type(src_field.ty) { @@ -43,7 +43,7 @@ fn into(params: &Parameters, fields: Vec<(Field, Field)>) -> TokenStream { continue; } }; - let target_field_type = match determine_field_type(dest_field.ty) { + let target_field_type = match determine_field_type(target_field.ty) { Ok(field) => field, Err(err) => { assignments.extend(vec![err]); @@ -59,7 +59,7 @@ fn into(params: &Parameters, fields: Vec<(Field, Field)>) -> TokenStream { target_type, "", quote! { - #dest_field_ident: src.#src_field_ident, + #target_field_ident: src.#src_field_ident, } ) } @@ -87,7 +87,7 @@ fn into(params: &Parameters, fields: Vec<(Field, Field)>) -> TokenStream { target_type, "", quote! { - #dest_field_ident: Some(src.#src_field_ident), + #target_field_ident: Some(src.#src_field_ident), } ) } @@ -107,23 +107,28 @@ fn into(params: &Parameters, fields: Vec<(Field, Field)>) -> TokenStream { ) => { // Handling the (Option, Option) case if is_equal_type(&inner_src_type, &inner_target_type) { - quote! { - #dest_field_ident: src.#src_field_ident, - } - // Handling the (src: Option>, dest: Option) case + equal_type_or_err!( + inner_src_type, + inner_target_type, + "", + quote! { + #target_field_ident: src.#src_field_ident, + } + ) + // Handling the (src: Option>, target: Option) case } else if is_equal_type(&inner_src_type, &outer_target_type) { err!( inner_src_type, "Inter-struct cannot 'into' an optional into a non-optional value." ) - // Handling the (src: Option<, dest: Option)> case + // Handling the (src: Option<, target: Option)> case } else { equal_type_or_err!( outer_src_type, inner_target_type, "", quote! { - #dest_field_ident: Some(src.#src_field_ident), + #target_field_ident: Some(src.#src_field_ident), } ) } diff --git a/codegen/src/generate/into/with_default.rs b/codegen/src/generate/into/with_default.rs index afcf022..8365d0d 100644 --- a/codegen/src/generate/into/with_default.rs +++ b/codegen/src/generate/into/with_default.rs @@ -32,9 +32,9 @@ pub(crate) fn impl_into_default(params: &Parameters, fields: Vec<(Field, Field)> fn into_default(params: &Parameters, fields: Vec<(Field, Field)>) -> TokenStream { let mut assignments = TokenStream::new(); - for (src_field, dest_field) in fields { + for (src_field, target_field) in fields { let src_field_ident = src_field.ident; - let dest_field_ident = dest_field.ident; + let target_field_ident = target_field.ident; // Find out, whether the fields are optional or not. let src_field_type = match determine_field_type(src_field.ty) { @@ -44,7 +44,7 @@ fn into_default(params: &Parameters, fields: Vec<(Field, Field)>) -> TokenStream continue; } }; - let target_field_type = match determine_field_type(dest_field.ty) { + let target_field_type = match determine_field_type(target_field.ty) { Ok(field) => field, Err(err) => { assignments.extend(vec![err]); @@ -60,7 +60,7 @@ fn into_default(params: &Parameters, fields: Vec<(Field, Field)>) -> TokenStream target_type, "", quote! { - #dest_field_ident: src.#src_field_ident, + #target_field_ident: src.#src_field_ident, } )) } @@ -77,7 +77,7 @@ fn into_default(params: &Parameters, fields: Vec<(Field, Field)>) -> TokenStream target_type, "", quote! { - #dest_field_ident: Some(src.#src_field_ident), + #target_field_ident: Some(src.#src_field_ident), } )), // Both fields are optional. It can now be either of these: @@ -97,19 +97,19 @@ fn into_default(params: &Parameters, fields: Vec<(Field, Field)>) -> TokenStream // Handling the (Option, Option) case if is_equal_type(&inner_src_type, &inner_target_type) { Some(quote! { - #dest_field_ident: src.#src_field_ident, + #target_field_ident: src.#src_field_ident, }) - // Handling the (src: Option>, dest: Option) case + // Handling the (src: Option>, target: Option) case } else if is_equal_type(&inner_src_type, &outer_target_type) { None - // Handling the (src: Option<, dest: Option)> case + // Handling the (src: Option<, target: Option)> case } else { Some(equal_type_or_err!( outer_src_type, inner_target_type, "", quote! { - #dest_field_ident: Some(src.#src_field_ident.clone()), + #target_field_ident: Some(src.#src_field_ident.clone()), } )) } diff --git a/codegen/src/generate/merge/borrowed.rs b/codegen/src/generate/merge/borrowed.rs index 8f4f8da..75dd64f 100644 --- a/codegen/src/generate/merge/borrowed.rs +++ b/codegen/src/generate/merge/borrowed.rs @@ -30,9 +30,9 @@ pub(crate) fn impl_borrowed(params: &Parameters, fields: Vec<(Field, Field)>) -> /// All fields must implement `Clone`. fn merge_ref(params: &Parameters, fields: Vec<(Field, Field)>) -> TokenStream { let mut merge_code = TokenStream::new(); - for (src_field, dest_field) in fields { + for (src_field, target_field) in fields { let src_field_ident = src_field.ident; - let dest_field_ident = dest_field.ident; + let target_field_ident = target_field.ident; // Find out, whether the fields are optional or not. let src_field_type = match determine_field_type(src_field.ty) { @@ -42,7 +42,7 @@ fn merge_ref(params: &Parameters, fields: Vec<(Field, Field)>) -> TokenStream { continue; } }; - let target_field_type = match determine_field_type(dest_field.ty) { + let target_field_type = match determine_field_type(target_field.ty) { Ok(field) => field, Err(err) => { merge_code.extend(vec![err]); @@ -58,7 +58,7 @@ fn merge_ref(params: &Parameters, fields: Vec<(Field, Field)>) -> TokenStream { target_type, "", quote! { - target.#dest_field_ident = self.#src_field_ident.clone(); + target.#target_field_ident = self.#src_field_ident.clone(); } ) } @@ -75,7 +75,7 @@ fn merge_ref(params: &Parameters, fields: Vec<(Field, Field)>) -> TokenStream { "Inner ", quote! { if let Some(value) = self.#src_field_ident.as_ref() { - target.#dest_field_ident = value.clone(); + target.#target_field_ident = value.clone(); } } ) @@ -92,7 +92,7 @@ fn merge_ref(params: &Parameters, fields: Vec<(Field, Field)>) -> TokenStream { target_type, "", quote! { - self.#dest_field_ident = Some(src.#src_field_ident.clone()); + self.#target_field_ident = Some(src.#src_field_ident.clone()); } ) } @@ -112,16 +112,26 @@ fn merge_ref(params: &Parameters, fields: Vec<(Field, Field)>) -> TokenStream { ) => { // Handling the (Option, Option) case if is_equal_type(&inner_src_type, &inner_target_type) { - quote! { - target.#dest_field_ident = self.#src_field_ident.clone(); - } + equal_type_or_err!( + inner_src_type, + inner_target_type, + "", + quote! { + target.#target_field_ident = self.#src_field_ident.clone(); + } + ) // Handling the (Option>, Option) case } else if is_equal_type(&inner_src_type, &outer_target_type) { - quote! { - if let Some(value) = self.#src_field_ident.as_ref() { - target.#dest_field_ident = value.clone(); + equal_type_or_err!( + inner_src_type, + outer_target_type, + "", + quote! { + if let Some(value) = self.#src_field_ident.as_ref() { + target.#target_field_ident = value.clone(); + } } - } + ) // Handling the (Option<, Option)> case } else { equal_type_or_err!( @@ -129,7 +139,7 @@ fn merge_ref(params: &Parameters, fields: Vec<(Field, Field)>) -> TokenStream { inner_target_type, "", quote! { - target.#dest_field_ident = Some(self.#src_field_ident.clone()); + target.#target_field_ident = Some(self.#src_field_ident.clone()); } ) } diff --git a/codegen/src/generate/merge/owned.rs b/codegen/src/generate/merge/owned.rs index e68e529..292aa06 100644 --- a/codegen/src/generate/merge/owned.rs +++ b/codegen/src/generate/merge/owned.rs @@ -28,9 +28,9 @@ pub(crate) fn impl_owned(params: &Parameters, fields: Vec<(Field, Field)>) -> To /// Generate the [inter_struct::merge::StructMerge::merge] function for the given structs. fn merge(params: &Parameters, fields: Vec<(Field, Field)>) -> TokenStream { let mut merge_code = TokenStream::new(); - for (src_field, dest_field) in fields { + for (src_field, target_field) in fields { let src_field_ident = src_field.ident; - let dest_field_ident = dest_field.ident; + let target_field_ident = target_field.ident; // Find out, whether the fields are optional or not. let src_field_type = match determine_field_type(src_field.ty) { @@ -40,7 +40,7 @@ fn merge(params: &Parameters, fields: Vec<(Field, Field)>) -> TokenStream { continue; } }; - let dest_field_type = match determine_field_type(dest_field.ty) { + let target_field_type = match determine_field_type(target_field.ty) { Ok(field) => field, Err(err) => { merge_code.extend(vec![err]); @@ -48,26 +48,51 @@ fn merge(params: &Parameters, fields: Vec<(Field, Field)>) -> TokenStream { } }; - let snippet = match (src_field_type, dest_field_type) { + let snippet = match (src_field_type, target_field_type) { // Both fields have the same type - (FieldType::Normal(_), FieldType::Normal(_)) => { - quote! { - dest.#dest_field_ident = self.#src_field_ident; - } + (FieldType::Normal(src_type), FieldType::Normal(target_type)) => { + equal_type_or_err!( + src_type, + target_type, + "", + quote! { + target.#target_field_ident = self.#src_field_ident; + } + ) } // The src is optional and needs to be `Some(T)` to be merged. - (FieldType::Optional { .. }, FieldType::Normal(_)) => { - quote! { - if let Some(value) = self.#src_field_ident { - dest.#dest_field_ident = value; + ( + FieldType::Optional { + inner: src_type, .. + }, + FieldType::Normal(target_type), + ) => { + equal_type_or_err!( + src_type, + target_type, + "", + quote! { + if let Some(value) = self.#src_field_ident { + target.#target_field_ident = value; + } } - } + ) } - // The dest is optional and needs to be wrapped in `Some(T)` to be merged. - (FieldType::Normal(_), FieldType::Optional { .. }) => { - quote! { - dest.#dest_field_ident = Some(self.#src_field_ident); - } + // The target is optional and needs to be wrapped in `Some(T)` to be merged. + ( + FieldType::Normal(src_type), + FieldType::Optional { + inner: target_type, .. + }, + ) => { + equal_type_or_err!( + src_type, + target_type, + "", + quote! { + target.#target_field_ident = Some(self.#src_field_ident); + } + ) } // Both fields are optional. It can now be either of these: // - (Option, Option) @@ -79,30 +104,40 @@ fn merge(params: &Parameters, fields: Vec<(Field, Field)>) -> TokenStream { outer: outer_src_type, }, FieldType::Optional { - inner: inner_dest_type, - outer: outer_dest_type, + inner: inner_target_type, + outer: outer_target_type, }, ) => { // Handling the (Option, Option) case - if is_equal_type(&inner_src_type, &inner_dest_type) { - quote! { - dest.#dest_field_ident = self.#src_field_ident; - } + if is_equal_type(&inner_src_type, &inner_target_type) { + equal_type_or_err!( + inner_src_type, + inner_target_type, + "", + quote! { + target.#target_field_ident = self.#src_field_ident; + } + ) // Handling the (Option>, Option) case - } else if is_equal_type(&inner_src_type, &outer_dest_type) { - quote! { - if let Some(value) = self.#src_field_ident { - dest.#dest_field_ident = value; + } else if is_equal_type(&inner_src_type, &outer_target_type) { + equal_type_or_err!( + inner_src_type, + outer_target_type, + "", + quote! { + if let Some(value) = self.#src_field_ident { + target.#target_field_ident = value; + } } - } + ) // Handling the (Option<, Option)> case } else { equal_type_or_err!( outer_src_type, - inner_dest_type, + inner_target_type, "", quote! { - dest.#dest_field_ident = Some(self.#src_field_ident); + target.#target_field_ident = Some(self.#src_field_ident); } ) } @@ -118,7 +153,7 @@ fn merge(params: &Parameters, fields: Vec<(Field, Field)>) -> TokenStream { let target_path = ¶ms.target_path; quote! { - fn merge_into(self, dest: &mut #target_path) { + fn merge_into(self, target: &mut #target_path) { #merge_code } } diff --git a/testing/tests/merge/incompatible_type.rs b/testing/tests/merge/incompatible_type.rs index 42c02c5..948b7e2 100644 --- a/testing/tests/merge/incompatible_type.rs +++ b/testing/tests/merge/incompatible_type.rs @@ -4,13 +4,15 @@ use inter_struct::prelude::*; #[derive(InterStruct)] #[merge("crate::MergeStruct")] pub struct FromStruct { - pub normal: String, + pub normal: i32, pub optional: i32, + pub optional_optional: Option>, } pub struct MergeStruct { pub normal: String, pub optional: Option, + pub optional_optional: Option>, } fn main() {} diff --git a/testing/tests/merge/incompatible_type.stderr b/testing/tests/merge/incompatible_type.stderr index 83ea491..35e5ac8 100644 --- a/testing/tests/merge/incompatible_type.stderr +++ b/testing/tests/merge/incompatible_type.stderr @@ -1,9 +1,17 @@ -error[E0308]: mismatched types - --> tests/merge/incompatible_type.rs:4:10 +error: Type 'i32 cannot be merged into field of type 'String'. + --> tests/merge/incompatible_type.rs:7:17 | -4 | #[derive(InterStruct)] - | ^^^^^^^^^^^- help: try using a conversion method: `.to_string()` - | | - | expected struct `String`, found `i32` +7 | pub normal: i32, + | ^^^ + +error: Type 'i32 cannot be merged into field of type 'String'. + --> tests/merge/incompatible_type.rs:8:19 | - = note: this error originates in the derive macro `InterStruct` (in Nightly builds, run with -Z macro-backtrace for more info) +8 | pub optional: i32, + | ^^^ + +error: Type 'Option < Option < i32 > > cannot be merged into field of type 'Option < String >'. + --> tests/merge/incompatible_type.rs:9:28 + | +9 | pub optional_optional: Option>, + | ^^^^^^ diff --git a/testing/tests/stub_declarations/lib.rs b/testing/tests/stub_declarations/lib.rs index 5036a0f..bf835ee 100644 --- a/testing/tests/stub_declarations/lib.rs +++ b/testing/tests/stub_declarations/lib.rs @@ -6,4 +6,5 @@ pub struct IntoStruct { pub struct MergeStruct { pub normal: String, pub optional: Option, + pub optional_optional: Option>, }