diff --git a/core/src/main/java/software/amazon/smithy/java/core/schema/DeferredMemberSchema.java b/core/src/main/java/software/amazon/smithy/java/core/schema/DeferredMemberSchema.java index 1af06f294..637f90000 100644 --- a/core/src/main/java/software/amazon/smithy/java/core/schema/DeferredMemberSchema.java +++ b/core/src/main/java/software/amazon/smithy/java/core/schema/DeferredMemberSchema.java @@ -15,11 +15,13 @@ final class DeferredMemberSchema extends Schema { private final SchemaBuilder target; private final TraitMap directTraits; + private final long requiredByValidationBitmask; DeferredMemberSchema(MemberSchemaBuilder builder) { super(builder); this.target = builder.targetBuilder; this.directTraits = builder.directTraits; + this.requiredByValidationBitmask = builder.requiredByValidationBitmask; } @Override @@ -44,7 +46,9 @@ public int requiredMemberCount() { @Override long requiredByValidationBitmask() { - return memberTarget().requiredByValidationBitmask(); + // The bitmask applies to the member itself, not its target schema, so this should not be forwarded + // to memberTarget as the previous accessors do. + return requiredByValidationBitmask; } @Override diff --git a/core/src/main/java/software/amazon/smithy/java/core/schema/MemberSchema.java b/core/src/main/java/software/amazon/smithy/java/core/schema/MemberSchema.java index f91eb2d4b..ace6ab09c 100644 --- a/core/src/main/java/software/amazon/smithy/java/core/schema/MemberSchema.java +++ b/core/src/main/java/software/amazon/smithy/java/core/schema/MemberSchema.java @@ -16,18 +16,12 @@ final class MemberSchema extends Schema { private final Schema target; private final TraitMap directTraits; - private final long requiredStructureMemberBitfield; private final long requiredByValidationBitmask; MemberSchema(MemberSchemaBuilder builder) { super(builder); this.target = builder.target; this.directTraits = builder.directTraits; - this.requiredStructureMemberBitfield = SchemaBuilder.computeRequiredBitField( - type(), - target.requiredMemberCount(), - builder.target.members(), - Schema::requiredByValidationBitmask); this.requiredByValidationBitmask = builder.requiredByValidationBitmask; } @@ -68,7 +62,7 @@ long requiredByValidationBitmask() { @Override long requiredStructureMemberBitfield() { - return requiredStructureMemberBitfield; + return target.requiredStructureMemberBitfield(); } @Override diff --git a/core/src/test/java/software/amazon/smithy/java/core/schema/ValidatorTest.java b/core/src/test/java/software/amazon/smithy/java/core/schema/ValidatorTest.java index 1b005de64..6bddc4a2f 100644 --- a/core/src/test/java/software/amazon/smithy/java/core/schema/ValidatorTest.java +++ b/core/src/test/java/software/amazon/smithy/java/core/schema/ValidatorTest.java @@ -131,6 +131,39 @@ public void detectsTooDeepRecursion() { assertThat(errors.get(0).path(), equalTo("/")); } + @Test + public void detectsPresenceForNestedRecursiveFields() { + var innerBuilder = Schema.structureBuilder(ShapeId.from("smithy.example#Inner")) + .putMember("foo", PreludeSchemas.STRING, new RequiredTrait()); + var innerSchema = innerBuilder.build(); + + var innerInstance = TestHelper.create(innerSchema, (schema, serializer) -> { + serializer.writeString(schema.member("foo"), "bar"); + }); + + var rootSchemaWithMember = Schema.structureBuilder(ShapeId.from("smithy.example#Root")) + .putMember("inner", innerSchema, new RequiredTrait()) + .build(); + var rootInstanceWithMember = TestHelper.create(rootSchemaWithMember, (schema, serializer) -> { + serializer.writeStruct(schema.member("inner"), innerInstance); + }); + + var rootSchemaWithDeferredMember = Schema.structureBuilder(ShapeId.from("smithy.example#Root")) + .putMember("inner", innerBuilder, new RequiredTrait()) + .build(); + var rootInstanceWithDeferredMember = TestHelper.create(rootSchemaWithDeferredMember, (schema, serializer) -> { + serializer.writeStruct(schema.member("inner"), innerInstance); + }); + + var validator = Validator.builder().build(); + + var memberErrors = validator.validate(rootInstanceWithMember); + assertThat(memberErrors, empty()); + + var deferredMemberErrors = validator.validate(rootInstanceWithDeferredMember); + assertThat(deferredMemberErrors, empty()); + } + private List createListSchemas(int depth) { List schemas = new ArrayList<>(depth); for (int i = depth; i > 0; i--) {