Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline

### Fixed

* Reject generator aliases whose final passthrough policy makes an inherited
field collide with the generated additional-properties member.
* Preserve round trips for discriminated map codecs with strict output schemas,
including values used as another codec's output, without narrowing permissive
intermediate output policies.
Expand Down
2 changes: 2 additions & 0 deletions packages/ack_generator/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@

### Fixed

* Validate inherited field names against an alias's final capture policy before
generation, rejecting collisions with the additional-properties member.
* Reject unproven object-shape changes inside preserved boundaries, including
aliases and fluent codecs, rather than copying opaque values as JSON.
* Preserve native codec boundary types when inferring `Ack.preserveBoundary`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -388,16 +388,6 @@ final class SchemaModelGraphBuilder extends ModelGraphBuilderBase {
element: declaration.element,
);
}
final problem = ackSchemaFirstFieldNameProblem(
jsonKey,
capturesExtras: additionalProperties,
);
if (problem != null) {
throw InvalidGenerationSource(
'$path.$jsonKey $problem',
element: declaration.element,
);
}
final fieldPath = '$path.$jsonKey';
final fieldChain = parseSchemaChain(entry.value);
final fieldMetadata = await _schemaMetadata(
Expand Down Expand Up @@ -1239,6 +1229,16 @@ final class SchemaModelGraphBuilder extends ModelGraphBuilderBase {
}
if (node is AckObjectModelNode) {
for (final field in node.fields) {
final problem = ackSchemaFirstFieldNameProblem(
field.dartName,
capturesExtras: node.additionalProperties,
);
if (problem != null) {
throw InvalidGenerationSource(
'${node.id.declarationName}.${field.dartName} $problem',
element: declaration.element,
);
}
checkMember(field.dartName);
}
if (node.discriminatorKey case final discriminator?) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,61 @@ final aliasSchema = originalSchema;
);
});

for (final alias in [
'final openSchema = baseSchema.passthrough();',
'final openSchema = baseSchema.copyWith(additionalProperties: true);',
'ObjectSchema get openSchema => baseSchema.passthrough();',
'''final middleSchema = baseSchema;
@Schemable()
final openSchema = middleSchema.passthrough();''',
]) {
test('rejects inherited capture-member collisions: $alias', () async {
await _expectFailure(
'''
$_head
@Schemable()
final baseSchema = Ack.object({'additionalProperties': Ack.string()});
@Schemable()
$alias
''',
['openSchema.additionalProperties', 'additional-properties member'],
);
});
}

for (final overrides in [
'.passthrough().strict()',
'.copyWith(additionalProperties: true).copyWith(additionalProperties: false)',
]) {
test('permits inherited additionalProperties after $overrides', () async {
final readerWriter = TestReaderWriter(rootPackage: 'test_pkg');
await readerWriter.testing.loadIsolateSources();
await testBuilder(
ackModelBuilder(BuilderOptions.empty),
{
'test_pkg|lib/models.dart':
'''
$_head
@Schemable()
final baseSchema = Ack.object({'additionalProperties': Ack.string()});
@Schemable()
final aliasSchema = baseSchema$overrides;
''',
},
readerWriter: readerWriter,
outputs: {
'test_pkg|lib/models.g.dart': decodedMatches(
allOf(
contains('class Alias'),
contains('final String additionalProperties;'),
isNot(contains('Map<String, Object?> additionalProperties')),
),
),
},
);
});
}

for (final discriminator in ['Pet', 'Cat']) {
test('rejects discriminator matching class $discriminator', () async {
await _expectFailure(
Expand Down
Loading