diff --git a/Cargo.lock b/Cargo.lock index bf21ae31..e604a482 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -182,6 +182,26 @@ dependencies = [ "parity-scale-codec", ] +[[package]] +name = "const_format" +version = "0.2.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "126f97965c8ad46d6d9163268ff28432e8f6a1196a55578867832e3049df63dd" +dependencies = [ + "const_format_proc_macros", +] + +[[package]] +name = "const_format_proc_macros" +version = "0.2.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d57c2eccfb16dbac1f4e61e206105db5820c9d26c3c472bc17c774259ef7744" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + [[package]] name = "criterion" version = "0.4.0" @@ -582,6 +602,7 @@ dependencies = [ "bitvec", "byte-slice-cast", "bytes", + "const_format", "criterion", "generic-array", "impl-trait-for-tuples", @@ -1070,6 +1091,12 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5bd2fe26506023ed7b5e1e315add59d6f584c621d037f9368fea9cfb988f368c" +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + [[package]] name = "version_check" version = "0.9.3" diff --git a/Cargo.toml b/Cargo.toml index d8f013db..78b0e57f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,6 +20,7 @@ byte-slice-cast = { version = "1.2.2", default-features = false } generic-array = { version = "0.14.7", optional = true } arbitrary = { version = "1.4.1", features = ["derive"], optional = true } impl-trait-for-tuples = "0.2.2" +const_format = { version = "0.2.34" } [dev-dependencies] criterion = "0.4.0" diff --git a/derive/src/decode.rs b/derive/src/decode.rs index 262371c8..7ea9c300 100644 --- a/derive/src/decode.rs +++ b/derive/src/decode.rs @@ -70,10 +70,13 @@ pub fn quote( }, } }); - let recurse_indices = - variants.iter().enumerate().map(|(i, v)| utils::variant_index(v, i)); + let recurse_indices = variants + .iter() + .enumerate() + .map(|(i, v)| (v.ident.clone(), utils::variant_index(v, i))); - let const_eval_check = utils::const_eval_check_variant_indexes(recurse_indices); + let const_eval_check = + utils::const_eval_check_variant_indexes(recurse_indices, crate_path); let read_byte_err_msg = format!("Could not decode `{type_name}`, failed to read variant byte"); diff --git a/derive/src/encode.rs b/derive/src/encode.rs index 403a4da9..52c12ebe 100644 --- a/derive/src/encode.rs +++ b/derive/src/encode.rs @@ -338,7 +338,7 @@ fn impl_encode(data: &Data, type_name: &Ident, crate_path: &syn::Path) -> TokenS } }; - [hinting, encoding, index] + (hinting, encoding, index, name.clone()) }, Fields::Unnamed(ref fields) => { let fields = &fields.unnamed; @@ -371,7 +371,7 @@ fn impl_encode(data: &Data, type_name: &Ident, crate_path: &syn::Path) -> TokenS } }; - [hinting, encoding, index] + (hinting, encoding, index, name.clone()) }, Fields::Unit => { let hinting = quote_spanned! { f.span() => @@ -387,14 +387,14 @@ fn impl_encode(data: &Data, type_name: &Ident, crate_path: &syn::Path) -> TokenS } }; - [hinting, encoding, index] + (hinting, encoding, index, name.clone()) }, } }); - let recurse_hinting = recurse.clone().map(|[hinting, _, _]| hinting); - let recurse_encoding = recurse.clone().map(|[_, encoding, _]| encoding); - let recurse_indices = recurse.clone().map(|[_, _, index]| index); + let recurse_hinting = recurse.clone().map(|(hinting, _, _, _)| hinting); + let recurse_encoding = recurse.clone().map(|(_, encoding, _, _)| encoding); + let recurse_variant_indices = recurse.clone().map(|(_, _, index, name)| (name, index)); let hinting = quote! { // The variant index uses 1 byte. @@ -404,7 +404,8 @@ fn impl_encode(data: &Data, type_name: &Ident, crate_path: &syn::Path) -> TokenS } }; - let const_eval_check = const_eval_check_variant_indexes(recurse_indices); + let const_eval_check = + const_eval_check_variant_indexes(recurse_variant_indices, crate_path); let encoding = quote! { #const_eval_check diff --git a/derive/src/utils.rs b/derive/src/utils.rs index a96bb298..59da171d 100644 --- a/derive/src/utils.rs +++ b/derive/src/utils.rs @@ -39,24 +39,55 @@ where } pub fn const_eval_check_variant_indexes( - recurse_indices: impl Iterator, + recurse_variant_indices: impl Iterator, + crate_path: &syn::Path, ) -> TokenStream { + let mut recurse_indices = vec![]; + for (ident, index) in recurse_variant_indices { + let ident_str = ident.to_string(); + recurse_indices.push(quote! { (#index, #ident_str) }); + } + let len = recurse_indices.len(); + + if len == 0 { + return quote! {}; + } + quote! { const _: () = { - let indices = [#( #recurse_indices ,)*]; - let len = indices.len(); - - // Check each pair for uniqueness - let mut i = 0; - while i < len { - let mut j = i + 1; - while j < len { - if indices[i] == indices[j] { - ::core::panic!("Found Variants that have duplicate indexes. Use different indexes for each variant"); - } - j += 1; - } - i += 1; + const indices: [(usize, &'static str); #len] = [#( #recurse_indices ,)*]; + + // Returns if there is duplicate, and if there is some the duplicate indexes. + const fn duplicate_info(array: &[(usize, &'static str); #len]) -> (bool, usize, usize) { + let len = array.len(); + let mut i = 0; + while i < len { + let mut j = i + 1; + while j < len { + if array[i].0 == array[j].0 { + return (true, i, j); + } + j += 1; + } + i += 1; + } + (false, 0, 0) + } + + const DUP_INFO: (bool, usize, usize) = duplicate_info(&indices); + + if DUP_INFO.0 { + let msg = #crate_path::__private::concatcp!( + "Found variants that have duplicate indexes. Both `", + indices[DUP_INFO.1].1, + "` and `", + indices[DUP_INFO.2].1, + "` have the index `", + indices[DUP_INFO.1].0, + "`. Use different indexes for each variant." + ); + + ::core::panic!("{}", msg); } }; } diff --git a/src/lib.rs b/src/lib.rs index f4152ddb..8370c95f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -41,6 +41,14 @@ pub mod alloc { pub use std::{alloc, borrow, boxed, collections, rc, string, sync, vec}; } +/// Private module to reexport items used by derive macros. +// We don't feature gate this module with `derive` to avoid compilation error when +// `parity-scale-codec-derive` is used on its own and this crate doesn't have the feature enabled. +#[doc(hidden)] +pub mod __private { + pub use const_format::concatcp; +} + #[cfg(feature = "bit-vec")] mod bit_vec; mod btree_utils; diff --git a/tests/scale_codec_ui/codec_discriminiant_variant_counted_in_default_index.stderr b/tests/scale_codec_ui/codec_discriminiant_variant_counted_in_default_index.stderr index cd6a11e2..732a9a9f 100644 --- a/tests/scale_codec_ui/codec_discriminiant_variant_counted_in_default_index.stderr +++ b/tests/scale_codec_ui/codec_discriminiant_variant_counted_in_default_index.stderr @@ -2,7 +2,7 @@ error[E0080]: evaluation of constant value failed --> tests/scale_codec_ui/codec_discriminiant_variant_counted_in_default_index.rs:1:10 | 1 | #[derive(::parity_scale_codec::Decode, ::parity_scale_codec::Encode)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'Found Variants that have duplicate indexes. Use different indexes for each variant', $DIR/tests/scale_codec_ui/codec_discriminiant_variant_counted_in_default_index.rs:1:10 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'Found variants that have duplicate indexes. Both `A` and `B` have the index `1`. Use different indexes for each variant.', $DIR/tests/scale_codec_ui/codec_discriminiant_variant_counted_in_default_index.rs:1:10 | = note: this error originates in the macro `$crate::panic::panic_2021` which comes from the expansion of the macro `::core::panic` (in Nightly builds, run with -Z macro-backtrace for more info) @@ -10,7 +10,7 @@ error[E0080]: evaluation of constant value failed --> tests/scale_codec_ui/codec_discriminiant_variant_counted_in_default_index.rs:1:40 | 1 | #[derive(::parity_scale_codec::Decode, ::parity_scale_codec::Encode)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'Found Variants that have duplicate indexes. Use different indexes for each variant', $DIR/tests/scale_codec_ui/codec_discriminiant_variant_counted_in_default_index.rs:1:40 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'Found variants that have duplicate indexes. Both `A` and `B` have the index `1`. Use different indexes for each variant.', $DIR/tests/scale_codec_ui/codec_discriminiant_variant_counted_in_default_index.rs:1:40 | = note: this error originates in the macro `$crate::panic::panic_2021` which comes from the expansion of the macro `::core::panic` (in Nightly builds, run with -Z macro-backtrace for more info) @@ -18,7 +18,7 @@ error[E0080]: evaluation of constant value failed --> tests/scale_codec_ui/codec_discriminiant_variant_counted_in_default_index.rs:8:10 | 8 | #[derive(::parity_scale_codec::Decode, ::parity_scale_codec::Encode)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'Found Variants that have duplicate indexes. Use different indexes for each variant', $DIR/tests/scale_codec_ui/codec_discriminiant_variant_counted_in_default_index.rs:8:10 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'Found variants that have duplicate indexes. Both `A` and `B` have the index `1`. Use different indexes for each variant.', $DIR/tests/scale_codec_ui/codec_discriminiant_variant_counted_in_default_index.rs:8:10 | = note: this error originates in the macro `$crate::panic::panic_2021` which comes from the expansion of the macro `::core::panic` (in Nightly builds, run with -Z macro-backtrace for more info) @@ -26,6 +26,6 @@ error[E0080]: evaluation of constant value failed --> tests/scale_codec_ui/codec_discriminiant_variant_counted_in_default_index.rs:8:40 | 8 | #[derive(::parity_scale_codec::Decode, ::parity_scale_codec::Encode)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'Found Variants that have duplicate indexes. Use different indexes for each variant', $DIR/tests/scale_codec_ui/codec_discriminiant_variant_counted_in_default_index.rs:8:40 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'Found variants that have duplicate indexes. Both `A` and `B` have the index `1`. Use different indexes for each variant.', $DIR/tests/scale_codec_ui/codec_discriminiant_variant_counted_in_default_index.rs:8:40 | = note: this error originates in the macro `$crate::panic::panic_2021` which comes from the expansion of the macro `::core::panic` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/scale_codec_ui/codec_duplicate_index.stderr b/tests/scale_codec_ui/codec_duplicate_index.stderr index 2848d4f8..573588fd 100644 --- a/tests/scale_codec_ui/codec_duplicate_index.stderr +++ b/tests/scale_codec_ui/codec_duplicate_index.stderr @@ -2,7 +2,7 @@ error[E0080]: evaluation of constant value failed --> tests/scale_codec_ui/codec_duplicate_index.rs:1:10 | 1 | #[derive(::parity_scale_codec::Decode, ::parity_scale_codec::Encode)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'Found Variants that have duplicate indexes. Use different indexes for each variant', $DIR/tests/scale_codec_ui/codec_duplicate_index.rs:1:10 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'Found variants that have duplicate indexes. Both `A` and `B` have the index `3`. Use different indexes for each variant.', $DIR/tests/scale_codec_ui/codec_duplicate_index.rs:1:10 | = note: this error originates in the macro `$crate::panic::panic_2021` which comes from the expansion of the macro `::core::panic` (in Nightly builds, run with -Z macro-backtrace for more info) @@ -10,7 +10,7 @@ error[E0080]: evaluation of constant value failed --> tests/scale_codec_ui/codec_duplicate_index.rs:1:40 | 1 | #[derive(::parity_scale_codec::Decode, ::parity_scale_codec::Encode)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'Found Variants that have duplicate indexes. Use different indexes for each variant', $DIR/tests/scale_codec_ui/codec_duplicate_index.rs:1:40 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'Found variants that have duplicate indexes. Both `A` and `B` have the index `3`. Use different indexes for each variant.', $DIR/tests/scale_codec_ui/codec_duplicate_index.rs:1:40 | = note: this error originates in the macro `$crate::panic::panic_2021` which comes from the expansion of the macro `::core::panic` (in Nightly builds, run with -Z macro-backtrace for more info) @@ -18,7 +18,7 @@ error[E0080]: evaluation of constant value failed --> tests/scale_codec_ui/codec_duplicate_index.rs:9:10 | 9 | #[derive(::parity_scale_codec::Decode, ::parity_scale_codec::Encode)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'Found Variants that have duplicate indexes. Use different indexes for each variant', $DIR/tests/scale_codec_ui/codec_duplicate_index.rs:9:10 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'Found variants that have duplicate indexes. Both `A` and `B` have the index `0`. Use different indexes for each variant.', $DIR/tests/scale_codec_ui/codec_duplicate_index.rs:9:10 | = note: this error originates in the macro `$crate::panic::panic_2021` which comes from the expansion of the macro `::core::panic` (in Nightly builds, run with -Z macro-backtrace for more info) @@ -26,6 +26,6 @@ error[E0080]: evaluation of constant value failed --> tests/scale_codec_ui/codec_duplicate_index.rs:9:40 | 9 | #[derive(::parity_scale_codec::Decode, ::parity_scale_codec::Encode)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'Found Variants that have duplicate indexes. Use different indexes for each variant', $DIR/tests/scale_codec_ui/codec_duplicate_index.rs:9:40 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'Found variants that have duplicate indexes. Both `A` and `B` have the index `0`. Use different indexes for each variant.', $DIR/tests/scale_codec_ui/codec_duplicate_index.rs:9:40 | = note: this error originates in the macro `$crate::panic::panic_2021` which comes from the expansion of the macro `::core::panic` (in Nightly builds, run with -Z macro-backtrace for more info)