diff --git a/borsh/src/schema.rs b/borsh/src/schema.rs index 47c8fa0f4..9657f1a5a 100644 --- a/borsh/src/schema.rs +++ b/borsh/src/schema.rs @@ -250,7 +250,12 @@ pub fn add_definition( match definitions.entry(declaration) { Entry::Occupied(occ) => { let existing_def = occ.get(); - assert_eq!(existing_def, &definition, "Redefining type schema for the same type name. Types with the same names are not supported."); + assert_eq!( + existing_def, + &definition, + "Redefining type schema for {}. Types with the same names are not supported.", + occ.key() + ); } Entry::Vacant(vac) => { vac.insert(definition); diff --git a/borsh/tests/test_schema_conflict.rs b/borsh/tests/test_schema_conflict.rs new file mode 100644 index 000000000..6d516091d --- /dev/null +++ b/borsh/tests/test_schema_conflict.rs @@ -0,0 +1,177 @@ +#![cfg_attr(not(feature = "std"), no_std)] +#![cfg(feature = "unstable__schema")] + +#[cfg(feature = "std")] +use std::collections::{BTreeMap, BTreeSet}; + +#[cfg(not(feature = "std"))] +extern crate alloc; +#[cfg(not(feature = "std"))] +use alloc::{ + collections::{BTreeMap, BTreeSet}, + format, + string::ToString, + vec::Vec, +}; + +use borsh::schema::{add_definition, Declaration, Definition, Fields}; +use borsh::BorshSchema; + +struct ConflictingSchema; + +impl BorshSchema for ConflictingSchema { + #[inline] + fn add_definitions_recursively(definitions: &mut BTreeMap) { + let fields = Fields::Empty; + let def = Definition::Struct { fields }; + add_definition(Self::declaration(), def, definitions); + } + #[inline] + fn declaration() -> Declaration { + "i64".into() + } +} + +#[test] +#[should_panic(expected = "Redefining type schema for i64")] +fn test_conflict() { + let mut defs = Default::default(); + as borsh::BorshSchema>::add_definitions_recursively(&mut defs); + + ::add_definitions_recursively(&mut defs); +} + +#[test] +#[should_panic(expected = "Redefining type schema for i64")] +fn test_implicit_conflict_vec() { + let mut defs = Default::default(); + as borsh::BorshSchema>::add_definitions_recursively(&mut defs); + as borsh::BorshSchema>::add_definitions_recursively(&mut defs); +} + +#[test] +#[should_panic(expected = "Redefining type schema for i64")] +fn test_implicit_conflict_range() { + let mut defs = Default::default(); + as borsh::BorshSchema>::add_definitions_recursively(&mut defs); + as borsh::BorshSchema>::add_definitions_recursively( + &mut defs, + ); +} + +#[test] +#[should_panic(expected = "Redefining type schema for i64")] +fn test_implicit_conflict_slice() { + let mut defs = Default::default(); + <[i64] as borsh::BorshSchema>::add_definitions_recursively(&mut defs); + <[ConflictingSchema] as borsh::BorshSchema>::add_definitions_recursively(&mut defs); +} + +#[test] +#[should_panic(expected = "Redefining type schema for i64")] +fn test_implicit_conflict_array() { + let mut defs = Default::default(); + <[i64; 10] as borsh::BorshSchema>::add_definitions_recursively(&mut defs); + <[ConflictingSchema; 10] as borsh::BorshSchema>::add_definitions_recursively(&mut defs); +} + +#[test] +#[should_panic(expected = "Redefining type schema for i64")] +fn test_implicit_conflict_option() { + let mut defs = Default::default(); + as borsh::BorshSchema>::add_definitions_recursively(&mut defs); + as borsh::BorshSchema>::add_definitions_recursively(&mut defs); +} + +#[allow(unused)] +#[derive(borsh::BorshSchema)] +struct GenericStruct { + field: T, +} + +#[test] +fn test_implicit_conflict_struct() { + let mut defs = Default::default(); + as borsh::BorshSchema>::add_definitions_recursively(&mut defs); + as borsh::BorshSchema>::add_definitions_recursively( + &mut defs, + ); + // NOTE: the contents of `defs` depend on the order of 2 above lines + // this loophole is needed to enable derives for recursive structs/enums +} + +#[allow(unused)] +#[derive(borsh::BorshSchema)] +struct SelfConflictingStruct { + field_1: i64, + field_2: ConflictingSchema, +} + +#[test] +#[should_panic(expected = "Redefining type schema for i64")] +fn test_implicit_conflict_self_conflicting_struct() { + let mut defs = Default::default(); + ::add_definitions_recursively(&mut defs); +} + +#[allow(unused)] +#[derive(borsh::BorshSchema)] +enum GenericEnum { + A { field: T }, + B(u64), +} + +#[test] +fn test_implicit_conflict_enum() { + let mut defs = Default::default(); + as borsh::BorshSchema>::add_definitions_recursively(&mut defs); + as borsh::BorshSchema>::add_definitions_recursively(&mut defs); + // NOTE: the contents of `defs` depend on the order of 2 above lines + // this loophole is needed to enable derives for recursive structs/enums +} + +#[allow(unused)] +#[derive(borsh::BorshSchema)] +enum SelfConflictingEnum { + A { field: i64 }, + B { field: ConflictingSchema }, +} + +#[test] +#[should_panic(expected = "Redefining type schema for i64")] +fn test_implicit_conflict_self_conflicting_enum() { + let mut defs = Default::default(); + ::add_definitions_recursively(&mut defs); +} + +#[test] +#[should_panic(expected = "Redefining type schema for i64")] +fn test_implicit_conflict_result() { + let mut defs = Default::default(); + as borsh::BorshSchema>::add_definitions_recursively(&mut defs); + as borsh::BorshSchema>::add_definitions_recursively(&mut defs); +} + +#[test] +#[should_panic(expected = "Redefining type schema for i64")] +fn test_implicit_conflict_btreemap() { + let mut defs = Default::default(); + as borsh::BorshSchema>::add_definitions_recursively(&mut defs); + as borsh::BorshSchema>::add_definitions_recursively(&mut defs); +} + +#[test] +#[should_panic(expected = "Redefining type schema for i64")] +fn test_implicit_conflict_btreeset() { + let mut defs = Default::default(); + as borsh::BorshSchema>::add_definitions_recursively(&mut defs); + as borsh::BorshSchema>::add_definitions_recursively(&mut defs); +} + +#[test] +#[should_panic(expected = "Redefining type schema for i64")] +fn test_implicit_conflict_tuple() { + let mut defs = Default::default(); + <(i64, u8) as borsh::BorshSchema>::add_definitions_recursively(&mut defs); + <(ConflictingSchema, u8) as borsh::BorshSchema>::add_definitions_recursively(&mut defs); +}