From 6a3bba1e869136da2baa9d4a0cf029eee0ea916f Mon Sep 17 00:00:00 2001 From: Graham Esau Date: Sat, 20 Mar 2021 18:45:29 +0000 Subject: [PATCH] Set additionalProperties to false on enums where appropriate --- CHANGELOG.md | 4 + schemars/tests/enum_deny_unknown_fields.rs | 106 ++++++++++ schemars/tests/expected/deprecated-enum.json | 3 +- .../tests/expected/doc_comments_enum.json | 3 +- .../expected/enum-adjacent-tagged-duf.json | 200 ++++++++++++++++++ .../tests/expected/enum-external-duf.json | 135 ++++++++++++ schemars/tests/expected/enum-external.json | 18 +- .../tests/expected/enum-internal-duf.json | 130 ++++++++++++ .../tests/expected/enum-untagged-duf.json | 77 +++++++ .../expected/schema_with-enum-external.json | 9 +- .../tests/expected/skip_enum_variants.json | 3 +- schemars_derive/src/schema_exprs.rs | 70 ++++-- 12 files changed, 727 insertions(+), 31 deletions(-) create mode 100644 schemars/tests/enum_deny_unknown_fields.rs create mode 100644 schemars/tests/expected/enum-adjacent-tagged-duf.json create mode 100644 schemars/tests/expected/enum-external-duf.json create mode 100644 schemars/tests/expected/enum-internal-duf.json create mode 100644 schemars/tests/expected/enum-untagged-duf.json diff --git a/CHANGELOG.md b/CHANGELOG.md index 4a0cd6c..b77390a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## **In-dev** - [0.8.1] +### Changed: +- Deriving JsonSchema on enums now sets `additionalProperties` to false on generated schemas wherever serde doesn't accept unknown properties. This includes non-unit variants of externally tagged enums, and struct-style variants of all enums that have the `deny_unknown_fields` attribute. + ## [0.8.0] - 2020-09-27 ### Added: - `visit::Visitor`, a trait for updating a schema and all schemas it contains recursively. A `SchemaSettings` can now contain a list of visitors. diff --git a/schemars/tests/enum_deny_unknown_fields.rs b/schemars/tests/enum_deny_unknown_fields.rs new file mode 100644 index 0000000..4fcb4bf --- /dev/null +++ b/schemars/tests/enum_deny_unknown_fields.rs @@ -0,0 +1,106 @@ +mod util; +use schemars::{JsonSchema, Map}; +use util::*; + +// Ensure that schemars_derive uses the full path to std::string::String +pub struct String; + +#[derive(Debug, JsonSchema)] +pub struct UnitStruct; + +#[derive(Debug, JsonSchema)] +pub struct Struct { + foo: i32, + bar: bool, +} + +// Outer container should always have additionalPropreties: false +// `Struct` variant should have additionalPropreties: false +#[derive(Debug, JsonSchema)] +#[schemars(rename_all = "camelCase", deny_unknown_fields)] +pub enum External { + UnitOne, + StringMap(Map<&'static str, &'static str>), + UnitStructNewType(UnitStruct), + StructNewType(Struct), + Struct { + foo: i32, + bar: bool, + }, + UnitTwo, + Tuple(i32, bool), + #[schemars(with = "i32")] + WithInt, +} + +#[test] +fn enum_external_tag() -> TestResult { + test_default_generated_schema::("enum-external-duf") +} + +// Only `Struct` variant should have additionalPropreties: false +#[derive(Debug, JsonSchema)] +#[schemars(tag = "typeProperty", deny_unknown_fields)] +pub enum Internal { + UnitOne, + StringMap(Map<&'static str, &'static str>), + UnitStructNewType(UnitStruct), + StructNewType(Struct), + Struct { + foo: i32, + bar: bool, + }, + UnitTwo, + #[schemars(with = "i32")] + WithInt, +} + +#[test] +fn enum_internal_tag() -> TestResult { + test_default_generated_schema::("enum-internal-duf") +} + +// Only `Struct` variant should have additionalPropreties: false +#[derive(Debug, JsonSchema)] +#[schemars(untagged, deny_unknown_fields)] +pub enum Untagged { + UnitOne, + StringMap(Map<&'static str, &'static str>), + UnitStructNewType(UnitStruct), + StructNewType(Struct), + Struct { + foo: i32, + bar: bool, + }, + Tuple(i32, bool), + #[schemars(with = "i32")] + WithInt, +} + +#[test] +fn enum_untagged() -> TestResult { + test_default_generated_schema::("enum-untagged-duf") +} + +// Outer container and `Struct` variant should have additionalPropreties: false +#[derive(Debug, JsonSchema)] +#[schemars(tag = "t", content = "c", deny_unknown_fields)] +pub enum Adjacent { + UnitOne, + StringMap(Map<&'static str, &'static str>), + UnitStructNewType(UnitStruct), + StructNewType(Struct), + Struct { + foo: i32, + bar: bool, + }, + Tuple(i32, bool), + UnitTwo, + #[schemars(with = "i32")] + WithInt, +} + +#[test] +fn enum_adjacent_tagged() -> TestResult { + test_default_generated_schema::("enum-adjacent-tagged-duf") +} diff --git a/schemars/tests/expected/deprecated-enum.json b/schemars/tests/expected/deprecated-enum.json index 41eaadf..f869be2 100644 --- a/schemars/tests/expected/deprecated-enum.json +++ b/schemars/tests/expected/deprecated-enum.json @@ -34,7 +34,8 @@ } } } - } + }, + "additionalProperties": false } ] } \ No newline at end of file diff --git a/schemars/tests/expected/doc_comments_enum.json b/schemars/tests/expected/doc_comments_enum.json index 1961955..edcb339 100644 --- a/schemars/tests/expected/doc_comments_enum.json +++ b/schemars/tests/expected/doc_comments_enum.json @@ -31,7 +31,8 @@ } } } - } + }, + "additionalProperties": false } ] } \ No newline at end of file diff --git a/schemars/tests/expected/enum-adjacent-tagged-duf.json b/schemars/tests/expected/enum-adjacent-tagged-duf.json new file mode 100644 index 0000000..011b378 --- /dev/null +++ b/schemars/tests/expected/enum-adjacent-tagged-duf.json @@ -0,0 +1,200 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Adjacent", + "anyOf": [ + { + "type": "object", + "required": [ + "t" + ], + "properties": { + "t": { + "type": "string", + "enum": [ + "UnitOne" + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "c", + "t" + ], + "properties": { + "t": { + "type": "string", + "enum": [ + "StringMap" + ] + }, + "c": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "c", + "t" + ], + "properties": { + "t": { + "type": "string", + "enum": [ + "UnitStructNewType" + ] + }, + "c": { + "$ref": "#/definitions/UnitStruct" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "c", + "t" + ], + "properties": { + "t": { + "type": "string", + "enum": [ + "StructNewType" + ] + }, + "c": { + "$ref": "#/definitions/Struct" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "c", + "t" + ], + "properties": { + "t": { + "type": "string", + "enum": [ + "Struct" + ] + }, + "c": { + "type": "object", + "required": [ + "bar", + "foo" + ], + "properties": { + "foo": { + "type": "integer", + "format": "int32" + }, + "bar": { + "type": "boolean" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "c", + "t" + ], + "properties": { + "t": { + "type": "string", + "enum": [ + "Tuple" + ] + }, + "c": { + "type": "array", + "items": [ + { + "type": "integer", + "format": "int32" + }, + { + "type": "boolean" + } + ], + "maxItems": 2, + "minItems": 2 + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "t" + ], + "properties": { + "t": { + "type": "string", + "enum": [ + "UnitTwo" + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "c", + "t" + ], + "properties": { + "t": { + "type": "string", + "enum": [ + "WithInt" + ] + }, + "c": { + "type": "integer", + "format": "int32" + } + }, + "additionalProperties": false + } + ], + "definitions": { + "UnitStruct": { + "type": "null" + }, + "Struct": { + "type": "object", + "required": [ + "bar", + "foo" + ], + "properties": { + "foo": { + "type": "integer", + "format": "int32" + }, + "bar": { + "type": "boolean" + } + } + } + } +} \ No newline at end of file diff --git a/schemars/tests/expected/enum-external-duf.json b/schemars/tests/expected/enum-external-duf.json new file mode 100644 index 0000000..4bbe7e4 --- /dev/null +++ b/schemars/tests/expected/enum-external-duf.json @@ -0,0 +1,135 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "External", + "anyOf": [ + { + "type": "string", + "enum": [ + "unitOne", + "unitTwo" + ] + }, + { + "type": "object", + "required": [ + "stringMap" + ], + "properties": { + "stringMap": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "unitStructNewType" + ], + "properties": { + "unitStructNewType": { + "$ref": "#/definitions/UnitStruct" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "structNewType" + ], + "properties": { + "structNewType": { + "$ref": "#/definitions/Struct" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "struct" + ], + "properties": { + "struct": { + "type": "object", + "required": [ + "bar", + "foo" + ], + "properties": { + "foo": { + "type": "integer", + "format": "int32" + }, + "bar": { + "type": "boolean" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "tuple" + ], + "properties": { + "tuple": { + "type": "array", + "items": [ + { + "type": "integer", + "format": "int32" + }, + { + "type": "boolean" + } + ], + "maxItems": 2, + "minItems": 2 + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "withInt" + ], + "properties": { + "withInt": { + "type": "integer", + "format": "int32" + } + }, + "additionalProperties": false + } + ], + "definitions": { + "UnitStruct": { + "type": "null" + }, + "Struct": { + "type": "object", + "required": [ + "bar", + "foo" + ], + "properties": { + "foo": { + "type": "integer", + "format": "int32" + }, + "bar": { + "type": "boolean" + } + } + } + } +} \ No newline at end of file diff --git a/schemars/tests/expected/enum-external.json b/schemars/tests/expected/enum-external.json index 5800e4f..cdc2fe4 100644 --- a/schemars/tests/expected/enum-external.json +++ b/schemars/tests/expected/enum-external.json @@ -21,7 +21,8 @@ "type": "string" } } - } + }, + "additionalProperties": false }, { "type": "object", @@ -32,7 +33,8 @@ "unitStructNewType": { "$ref": "#/definitions/UnitStruct" } - } + }, + "additionalProperties": false }, { "type": "object", @@ -43,7 +45,8 @@ "structNewType": { "$ref": "#/definitions/Struct" } - } + }, + "additionalProperties": false }, { "type": "object", @@ -67,7 +70,8 @@ } } } - } + }, + "additionalProperties": false }, { "type": "object", @@ -89,7 +93,8 @@ "maxItems": 2, "minItems": 2 } - } + }, + "additionalProperties": false }, { "type": "object", @@ -101,7 +106,8 @@ "type": "integer", "format": "int32" } - } + }, + "additionalProperties": false } ], "definitions": { diff --git a/schemars/tests/expected/enum-internal-duf.json b/schemars/tests/expected/enum-internal-duf.json new file mode 100644 index 0000000..8b6fef8 --- /dev/null +++ b/schemars/tests/expected/enum-internal-duf.json @@ -0,0 +1,130 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Internal", + "anyOf": [ + { + "type": "object", + "required": [ + "typeProperty" + ], + "properties": { + "typeProperty": { + "type": "string", + "enum": [ + "UnitOne" + ] + } + } + }, + { + "type": "object", + "required": [ + "typeProperty" + ], + "properties": { + "typeProperty": { + "type": "string", + "enum": [ + "StringMap" + ] + } + }, + "additionalProperties": { + "type": "string" + } + }, + { + "type": "object", + "required": [ + "typeProperty" + ], + "properties": { + "typeProperty": { + "type": "string", + "enum": [ + "UnitStructNewType" + ] + } + } + }, + { + "type": "object", + "required": [ + "bar", + "foo", + "typeProperty" + ], + "properties": { + "typeProperty": { + "type": "string", + "enum": [ + "StructNewType" + ] + }, + "foo": { + "type": "integer", + "format": "int32" + }, + "bar": { + "type": "boolean" + } + } + }, + { + "type": "object", + "required": [ + "bar", + "foo", + "typeProperty" + ], + "properties": { + "typeProperty": { + "type": "string", + "enum": [ + "Struct" + ] + }, + "foo": { + "type": "integer", + "format": "int32" + }, + "bar": { + "type": "boolean" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "typeProperty" + ], + "properties": { + "typeProperty": { + "type": "string", + "enum": [ + "UnitTwo" + ] + } + } + }, + { + "type": [ + "object", + "integer" + ], + "format": "int32", + "required": [ + "typeProperty" + ], + "properties": { + "typeProperty": { + "type": "string", + "enum": [ + "WithInt" + ] + } + } + } + ] +} \ No newline at end of file diff --git a/schemars/tests/expected/enum-untagged-duf.json b/schemars/tests/expected/enum-untagged-duf.json new file mode 100644 index 0000000..24397bf --- /dev/null +++ b/schemars/tests/expected/enum-untagged-duf.json @@ -0,0 +1,77 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Untagged", + "anyOf": [ + { + "type": "null" + }, + { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + { + "$ref": "#/definitions/UnitStruct" + }, + { + "$ref": "#/definitions/Struct" + }, + { + "type": "object", + "required": [ + "bar", + "foo" + ], + "properties": { + "foo": { + "type": "integer", + "format": "int32" + }, + "bar": { + "type": "boolean" + } + }, + "additionalProperties": false + }, + { + "type": "array", + "items": [ + { + "type": "integer", + "format": "int32" + }, + { + "type": "boolean" + } + ], + "maxItems": 2, + "minItems": 2 + }, + { + "type": "integer", + "format": "int32" + } + ], + "definitions": { + "UnitStruct": { + "type": "null" + }, + "Struct": { + "type": "object", + "required": [ + "bar", + "foo" + ], + "properties": { + "foo": { + "type": "integer", + "format": "int32" + }, + "bar": { + "type": "boolean" + } + } + } + } +} \ No newline at end of file diff --git a/schemars/tests/expected/schema_with-enum-external.json b/schemars/tests/expected/schema_with-enum-external.json index 2e7d415..364b5f3 100644 --- a/schemars/tests/expected/schema_with-enum-external.json +++ b/schemars/tests/expected/schema_with-enum-external.json @@ -19,7 +19,8 @@ } } } - } + }, + "additionalProperties": false }, { "type": "object", @@ -30,7 +31,8 @@ "newType": { "type": "boolean" } - } + }, + "additionalProperties": false }, { "type": "object", @@ -52,7 +54,8 @@ "maxItems": 2, "minItems": 2 } - } + }, + "additionalProperties": false } ] } \ No newline at end of file diff --git a/schemars/tests/expected/skip_enum_variants.json b/schemars/tests/expected/skip_enum_variants.json index c347df0..4acb2eb 100644 --- a/schemars/tests/expected/skip_enum_variants.json +++ b/schemars/tests/expected/skip_enum_variants.json @@ -18,7 +18,8 @@ "type": "number", "format": "float" } - } + }, + "additionalProperties": false } ] } \ No newline at end of file diff --git a/schemars_derive/src/schema_exprs.rs b/schemars_derive/src/schema_exprs.rs index 2ec3497..905f67a 100644 --- a/schemars_derive/src/schema_exprs.rs +++ b/schemars_derive/src/schema_exprs.rs @@ -9,7 +9,11 @@ pub fn expr_for_container(cont: &Container) -> TokenStream { Data::Struct(Style::Unit, _) => expr_for_unit_struct(), Data::Struct(Style::Newtype, fields) => expr_for_newtype_struct(&fields[0]), Data::Struct(Style::Tuple, fields) => expr_for_tuple_struct(fields), - Data::Struct(Style::Struct, fields) => expr_for_struct(fields, Some(&cont.serde_attrs)), + Data::Struct(Style::Struct, fields) => expr_for_struct( + fields, + cont.serde_attrs.default(), + cont.serde_attrs.deny_unknown_fields(), + ), Data::Enum(variants) => expr_for_enum(variants, &cont.serde_attrs), }; @@ -70,19 +74,26 @@ pub fn type_for_schema(field: &Field, local_id: usize) -> (syn::Type, Option TokenStream { + let deny_unknown_fields = cattrs.deny_unknown_fields(); let variants = variants .iter() .filter(|v| !v.serde_attrs.skip_deserializing()); + match cattrs.tag() { - TagType::External => expr_for_external_tagged_enum(variants), - TagType::None => expr_for_untagged_enum(variants), - TagType::Internal { tag } => expr_for_internal_tagged_enum(variants, tag), - TagType::Adjacent { tag, content } => expr_for_adjacent_tagged_enum(variants, tag, content), + TagType::External => expr_for_external_tagged_enum(variants, deny_unknown_fields), + TagType::None => expr_for_untagged_enum(variants, deny_unknown_fields), + TagType::Internal { tag } => { + expr_for_internal_tagged_enum(variants, tag, deny_unknown_fields) + } + TagType::Adjacent { tag, content } => { + expr_for_adjacent_tagged_enum(variants, tag, content, deny_unknown_fields) + } } } fn expr_for_external_tagged_enum<'a>( variants: impl Iterator>, + deny_unknown_fields: bool, ) -> TokenStream { let (unit_variants, complex_variants): (Vec<_>, Vec<_>) = variants.partition(|v| v.is_unit() && v.attrs.with.is_none()); @@ -104,7 +115,8 @@ fn expr_for_external_tagged_enum<'a>( schemas.extend(complex_variants.into_iter().map(|variant| { let name = variant.name(); - let sub_schema = expr_for_untagged_enum_variant(variant); + let sub_schema = expr_for_untagged_enum_variant(variant, deny_unknown_fields); + let schema_expr = schema_object(quote! { instance_type: Some(schemars::schema::InstanceType::Object.into()), object: Some(Box::new(schemars::schema::ObjectValidation { @@ -118,6 +130,7 @@ fn expr_for_external_tagged_enum<'a>( required.insert(#name.to_owned()); required }, + additional_properties: Some(Box::new(false.into())), ..Default::default() })), }); @@ -136,6 +149,7 @@ fn expr_for_external_tagged_enum<'a>( fn expr_for_internal_tagged_enum<'a>( variants: impl Iterator>, tag_name: &str, + deny_unknown_fields: bool, ) -> TokenStream { let variant_schemas = variants.map(|variant| { let name = variant.name(); @@ -163,7 +177,7 @@ fn expr_for_internal_tagged_enum<'a>( let doc_metadata = SchemaMetadata::from_attrs(&variant.attrs); let tag_schema = doc_metadata.apply_to_schema(tag_schema); - match expr_for_untagged_enum_variant_for_flatten(&variant) { + match expr_for_untagged_enum_variant_for_flatten(&variant, deny_unknown_fields) { Some(variant_schema) => quote! { #tag_schema.flatten(#variant_schema) }, @@ -179,9 +193,12 @@ fn expr_for_internal_tagged_enum<'a>( }) } -fn expr_for_untagged_enum<'a>(variants: impl Iterator>) -> TokenStream { +fn expr_for_untagged_enum<'a>( + variants: impl Iterator>, + deny_unknown_fields: bool, +) -> TokenStream { let schemas = variants.map(|variant| { - let schema_expr = expr_for_untagged_enum_variant(variant); + let schema_expr = expr_for_untagged_enum_variant(variant, deny_unknown_fields); let doc_metadata = SchemaMetadata::from_attrs(&variant.attrs); doc_metadata.apply_to_schema(schema_expr) }); @@ -198,12 +215,13 @@ fn expr_for_adjacent_tagged_enum<'a>( variants: impl Iterator>, tag_name: &str, content_name: &str, + deny_unknown_fields: bool, ) -> TokenStream { let schemas = variants.map(|variant| { let content_schema = if variant.is_unit() && variant.attrs.with.is_none() { None } else { - Some(expr_for_untagged_enum_variant(variant)) + Some(expr_for_untagged_enum_variant(variant, deny_unknown_fields)) }; let (add_content_to_props, add_content_to_required) = content_schema @@ -221,6 +239,14 @@ fn expr_for_adjacent_tagged_enum<'a>( enum_values: Some(vec![#name.into()]), }); + let set_additional_properties = if deny_unknown_fields { + quote! { + additional_properties: Some(Box::new(false.into())), + } + } else { + TokenStream::new() + }; + let outer_schema = schema_object(quote! { instance_type: Some(schemars::schema::InstanceType::Object.into()), object: Some(Box::new(schemars::schema::ObjectValidation { @@ -236,6 +262,7 @@ fn expr_for_adjacent_tagged_enum<'a>( #add_content_to_required required }, + #set_additional_properties ..Default::default() })), }); @@ -252,7 +279,7 @@ fn expr_for_adjacent_tagged_enum<'a>( }) } -fn expr_for_untagged_enum_variant(variant: &Variant) -> TokenStream { +fn expr_for_untagged_enum_variant(variant: &Variant, deny_unknown_fields: bool) -> TokenStream { if let Some(WithAttr::Type(with)) = &variant.attrs.with { return quote_spanned! {variant.original.span()=> gen.subschema_for::<#with>() @@ -263,11 +290,14 @@ fn expr_for_untagged_enum_variant(variant: &Variant) -> TokenStream { Style::Unit => expr_for_unit_struct(), Style::Newtype => expr_for_field(&variant.fields[0], true), Style::Tuple => expr_for_tuple_struct(&variant.fields), - Style::Struct => expr_for_struct(&variant.fields, None), + Style::Struct => expr_for_struct(&variant.fields, &SerdeDefault::None, deny_unknown_fields), } } -fn expr_for_untagged_enum_variant_for_flatten(variant: &Variant) -> Option { +fn expr_for_untagged_enum_variant_for_flatten( + variant: &Variant, + deny_unknown_fields: bool, +) -> Option { if let Some(WithAttr::Type(with)) = &variant.attrs.with { return Some(quote_spanned! {variant.original.span()=> <#with>::json_schema(gen) @@ -278,7 +308,7 @@ fn expr_for_untagged_enum_variant_for_flatten(variant: &Variant) -> Option return None, Style::Newtype => expr_for_field(&variant.fields[0], false), Style::Tuple => expr_for_tuple_struct(&variant.fields), - Style::Struct => expr_for_struct(&variant.fields, None), + Style::Struct => expr_for_struct(&variant.fields, &SerdeDefault::None, deny_unknown_fields), }) } @@ -307,17 +337,21 @@ fn expr_for_tuple_struct(fields: &[Field]) -> TokenStream { } } -fn expr_for_struct(fields: &[Field], cattrs: Option<&serde_attr::Container>) -> TokenStream { +fn expr_for_struct( + fields: &[Field], + default: &SerdeDefault, + deny_unknown_fields: bool, +) -> TokenStream { let (flattened_fields, property_fields): (Vec<_>, Vec<_>) = fields .iter() .filter(|f| !f.serde_attrs.skip_deserializing() || !f.serde_attrs.skip_serializing()) .partition(|f| f.serde_attrs.flatten()); - let set_container_default = cattrs.and_then(|c| match c.default() { + let set_container_default = match default { SerdeDefault::None => None, SerdeDefault::Default => Some(quote!(let container_default = Self::default();)), SerdeDefault::Path(path) => Some(quote!(let container_default = #path();)), - }); + }; let mut type_defs = Vec::new(); @@ -362,7 +396,6 @@ fn expr_for_struct(fields: &[Field], cattrs: Option<&serde_attr::Container>) -> }) .collect(); - let deny_unknown_fields = cattrs.map_or(false, |attrs| attrs.deny_unknown_fields()); let set_additional_properties = if deny_unknown_fields { quote! { schema_object.object().additional_properties = Some(Box::new(false.into())); @@ -370,7 +403,6 @@ fn expr_for_struct(fields: &[Field], cattrs: Option<&serde_attr::Container>) -> } else { TokenStream::new() }; - quote! { { #(#type_defs)*