From f797cbe0291f3e862ceb00914c6ab62529df9c7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Max=20Mittelst=C3=A4dt?= <62167861+TheGalitube@users.noreply.github.com> Date: Sun, 1 Nov 2026 09:39:49 +0000 Subject: [PATCH 1/2] Reject duplicate Kiwi field IDs Found while testing crates/format/src/fig/kiwi.rs:615 Co-Authored-By: GPT-5.6-Luna Co-Authored-By: GPT-6-Luna --- crates/format/src/fig/kiwi.rs | 6 ++++++ crates/format/src/fig/tests.rs | 5 +++++ 2 files changed, 11 insertions(+) diff --git a/crates/format/src/fig/kiwi.rs b/crates/format/src/fig/kiwi.rs index d34b3774d..26e8284e4 100644 --- a/crates/format/src/fig/kiwi.rs +++ b/crates/format/src/fig/kiwi.rs @@ -270,6 +270,9 @@ impl Schema { let ty = r.var_int()?; let array = r.byte()? & 1 != 0; let value = r.var_uint()?; + if kind == Kind::Message && fields.iter().any(|f: &Field| f.value == value) { + return err(format!("schema: duplicate field id {value} in {name}")); + } raw_types.push(ty); fields.push(Field { name: fname, ty: None, array, value }); } @@ -621,6 +624,9 @@ impl Schema { } .ok_or_else(|| KiwiError(format!("schema text: unknown type {base}")))?; let (name, value) = numbered(n.trim(), id)?; + if *kind == "message" && fields.iter().any(|f: &Field| f.value == value) { + return err(format!("schema text: duplicate field id {value} in {name}")); + } fields.push(Field { name, ty: Some(ty), array: t.ends_with("[]"), value }); } (if *kind == "struct" { Kind::Struct } else { Kind::Message }, fields) diff --git a/crates/format/src/fig/tests.rs b/crates/format/src/fig/tests.rs index 657f2f81d..50c50c3d0 100644 --- a/crates/format/src/fig/tests.rs +++ b/crates/format/src/fig/tests.rs @@ -562,6 +562,11 @@ fn oversized_grid_track_lists_warn_and_use_default_tracks() { assert!(warnings.iter().any(|w| w.contains("grid track list exceeds 1000 entries")), "{warnings:?}"); } +#[test] +fn duplicate_explicit_field_ids_are_ambiguous() { + assert!(Schema::parse("message M uint a=1, uint b=1").is_err()); +} + #[test] fn kiwi_round_trip() { let sc = schema(); From acbc988225753c6f7552f6b85884ff71e562e428 Mon Sep 17 00:00:00 2001 From: bflatastic Date: Sat, 7 Nov 2026 08:48:40 +0700 Subject: [PATCH 2/2] Schema test: its field ids no longer repeat, now that repeats are refused --- crates/format/src/fig/interop_tests.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/format/src/fig/interop_tests.rs b/crates/format/src/fig/interop_tests.rs index fa95d792b..4ad81cebc 100644 --- a/crates/format/src/fig/interop_tests.rs +++ b/crates/format/src/fig/interop_tests.rs @@ -403,10 +403,10 @@ fn the_schema_numbers_what_the_format_numbers() { #[test] fn schema_text_takes_numbers_and_refuses_bad_ones() { - let s = Schema::parse("enum E A=3 B C=9\nmessage M uint a=7, E b, string c=2").unwrap(); + let s = Schema::parse("enum E A=3 B C=9\nmessage M uint a=7, E b, string c=5").unwrap(); let values = |i: usize| s.defs[i].fields.iter().map(|f| (f.name.as_str(), f.value)).collect::>(); assert_eq!(values(0), [("A", 3), ("B", 1), ("C", 9)]); - assert_eq!(values(1), [("a", 7), ("b", 2), ("c", 2)]); + assert_eq!(values(1), [("a", 7), ("b", 2), ("c", 5)]); for bad in ["enum E A=", "enum E A=x", "enum E A=-1", "message M uint a=1.5", "message M uint a=99999999999", "enum E =", "message M uint ="] { assert!(Schema::parse(bad).is_err() || bad.ends_with('='), "{bad}"); }