Fix invalid Rust emitted for defaults on types with flattened properties - #1039
Fix invalid Rust emitted for defaults on types with flattened properties#1039danieleades wants to merge 1 commit into
Conversation
58287a2 to
995496c
Compare
ahl
left a comment
There was a problem hiding this comment.
yep! nice find. Did you encounter this with a real schema? If not, how did you hit this?
| #[serde(default, skip_serializing_if = "::std::option::Option::is_none")] | ||
| pub id: ::std::option::Option<::std::string::String>, | ||
| #[serde(flatten)] | ||
| pub extra: ::std::collections::HashMap<::std::string::String, ::std::string::String>, |
There was a problem hiding this comment.
I suppose the key to the map should actually be a newtype wrapper around a String that prohibits values that conflict with existing property names (here "id")...
There was a problem hiding this comment.
Yes, I think that is a separate correctness gap in how we model typed additionalProperties. The generated public map lets callers insert id, which can produce a duplicate or conflicting key during serialization even though deserialization routes id to the declared field. A key newtype or custom map wrapper that excludes declared property names would make that invariant explicit.
I would keep this PR scoped to the invalid Rust emission, since it is already a complete compile fix, and track the key-collision behavior separately with construction and round-trip tests.
…tput When generating a Rust value expression for a type-level `default` on a struct that has a `#[serde(flatten)]` property, the flattened property's name was interpolated as a string literal instead of an identifier, producing invalid Rust (e.g. `"extra": ...` inside a struct literal) that fails to compile and cannot be formatted by rustfmt. Use format_ident! for the flattened field, matching the sibling non-flattened path just above it. Added tests/schemas/flatten-default.json as a regression test, exercised by the existing schemas test harness (compiled via trybuild).
995496c to
69eab02
Compare
|
I did not encounter this in a production schema. I found it while tracing the whole-type-default path through |
Problem
When generating a Rust value expression for a type-level
defaulton a struct that has a#[serde(flatten)]property (e.g. an object withadditionalPropertiesalongside declared properties), the code intypify-impl/src/value.rsinterpolated the flattened property's name as a string literal instead of an identifier:Since
nameis aString,quote!renders it as a string literal ("extra": ...) rather than a field name, producing invalid Rust inside the struct literal. This causes rustfmt/compilation to fail for any generated default value on a struct with a flattened property.Fix
Build a proper identifier for the flattened field, matching the sibling non-flattened path a few lines above (
format_ident!("{}", &prop.name)).Testing
Added
typify/tests/schemas/flatten-default.json, a minimal schema with a struct-typed property carrying adefaultwhere the struct has both declared properties andadditionalProperties(flattened into a map). This is picked up automatically by the existingtest_schemasharness, which regenerates and rustfmts the output and then compiles it via trybuild.error: expected identifier, found "extra"(rustfmt).cargo test --workspace --lockedpasses, including trybuild compilation of the new golden file.cargo fmt --all -- --checkis clean.