Skip to content

Commit 428c276

Browse files
committed
fix: consistency improvements and more invalid config validations
1 parent 711cb86 commit 428c276

File tree

4 files changed

+55
-29
lines changed

4 files changed

+55
-29
lines changed

json_serializable/lib/src/decode_helper.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ mixin DecodeHelper implements HelperCore {
148148
String _createSealedFunctionExpressionBody() {
149149
assert(element.isSealed);
150150

151-
final implementations = sealedClassImplementations(element);
151+
final implementations = sealedSubClasses(element);
152152

153153
final discriminator = config.unionDiscriminator;
154154

json_serializable/lib/src/encoder_helper.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ mixin EncodeHelper implements HelperCore {
141141
String _createSealedFunctionExpressionBody() {
142142
assert(element.isSealed);
143143

144-
final implementations = sealedClassImplementations(element);
144+
final implementations = sealedSubClasses(element);
145145

146146
final discriminator = config.unionDiscriminator;
147147

json_serializable/lib/src/generator_helper.dart

Lines changed: 52 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import 'package:analyzer/dart/element/element2.dart';
66
import 'package:build/build.dart';
7+
import 'package:collection/collection.dart';
78
import 'package:source_gen/source_gen.dart';
89

910
import '../type_helper.dart';
@@ -48,13 +49,25 @@ class GeneratorHelper extends HelperCore with EncodeHelper, DecodeHelper {
4849
);
4950
}
5051

51-
final sealedSuperClassesOrEmpty = sealedSuperClasses(element);
52+
final sealedSupersAndConfigs = sealedSuperClasses(element).map(
53+
(superClass) => (
54+
classElement: superClass,
55+
config: jsonSerializableConfig(superClass, _generator),
56+
),
57+
);
5258

53-
final sealedDiscriminators = sealedSuperClassesOrEmpty
54-
.map((sealedClass) => jsonSerializableConfig(sealedClass, _generator))
55-
.map((config) => config?.unionDiscriminator);
59+
if (sealedSupersAndConfigs.isNotEmpty &&
60+
sealedSupersAndConfigs.any((e) => e.config == null)) {
61+
throw InvalidGenerationSourceError(
62+
'The class `${element.displayName}` is annotated '
63+
'with `JsonSerializable` but its superclass is not annotated '
64+
'with `JsonSerializable`.',
65+
todo: 'Add `@JsonSerializable` annotation to the sealed class.',
66+
element: element,
67+
);
68+
}
5669

57-
if ((sealedSuperClassesOrEmpty.isNotEmpty || element.isSealed) &&
70+
if ((sealedSupersAndConfigs.isNotEmpty || element.isSealed) &&
5871
config.genericArgumentFactories) {
5972
throw InvalidGenerationSourceError(
6073
'The class `${element.displayName}` is annotated '
@@ -68,32 +81,43 @@ class GeneratorHelper extends HelperCore with EncodeHelper, DecodeHelper {
6881
);
6982
}
7083

84+
if (sealedSupersAndConfigs.firstWhereOrNull(
85+
(e) => e.config?.unionDiscriminator == config.unionDiscriminator,
86+
)
87+
case final conflictingSuper? when element.isSealed) {
88+
throw InvalidGenerationSource(
89+
'The classes `${conflictingSuper.classElement.displayName}` and '
90+
'${element.displayName} are nested sealed classes, but they have '
91+
'the same discriminator ${config.unionDiscriminator}.',
92+
todo:
93+
'Rename one of the discriminators with `unionDiscriminator` '
94+
'field of `@JsonSerializable`.',
95+
);
96+
}
97+
98+
if (sealedSupersAndConfigs.firstWhereOrNull(
99+
(e) => e.config?.createToJson != config.createToJson,
100+
)
101+
case final diffSuper?) {
102+
throw InvalidGenerationSourceError(
103+
'The class `${diffSuper.classElement.displayName}` is sealed but its '
104+
'subclass `${element.displayName}` has a different '
105+
'`createToJson` option than the base class.',
106+
element: element,
107+
);
108+
}
109+
71110
if (element.isSealed) {
72-
if (sealedDiscriminators.contains(config.unionDiscriminator)) {
73-
throw InvalidGenerationSource(
74-
'Nested sealed classes cannot have the same discriminator.',
75-
todo:
76-
'Rename one of the discriminators with `unionDiscriminator` '
77-
'field in `@JsonSerializable`.',
78-
);
79-
}
80-
sealedClassImplementations(element).forEach((impl) {
81-
final annotationConfig = jsonSerializableConfig(impl, _generator);
111+
sealedSubClasses(element).forEach((sub) {
112+
final annotationConfig = jsonSerializableConfig(sub, _generator);
82113

83114
if (annotationConfig == null) {
84115
throw InvalidGenerationSourceError(
85116
'The class `${element.displayName}` is sealed but its '
86-
'implementation `${impl.displayName}` is not annotated with '
117+
'subclass `${sub.displayName}` is not annotated with '
87118
'`JsonSerializable`.',
88-
todo: 'Add `@JsonSerializable` annotation to ${impl.displayName}.',
89-
);
90-
}
91-
92-
if (annotationConfig.createToJson != config.createToJson) {
93-
throw InvalidGenerationSourceError(
94-
'The class `${element.displayName}` is sealed but its '
95-
'implementation `${impl.displayName}` has a different '
96-
'`createToJson` option than the base class.',
119+
todo: 'Add `@JsonSerializable` annotation to ${sub.displayName}.',
120+
element: sub,
97121
);
98122
}
99123
});
@@ -166,7 +190,9 @@ class GeneratorHelper extends HelperCore with EncodeHelper, DecodeHelper {
166190
..fold(<String>{}, (Set<String> set, fe) {
167191
final jsonKey = nameAccess(fe);
168192

169-
if (sealedDiscriminators.contains(jsonKey)) {
193+
if (sealedSupersAndConfigs.any(
194+
(e) => e.config?.unionDiscriminator == jsonKey,
195+
)) {
170196
throw InvalidGenerationSourceError(
171197
'The JSON key "$jsonKey" is conflicting with the discriminator '
172198
'of sealed superclass ',

json_serializable/lib/src/utils.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ ConstructorElement2 constructorByName(ClassElement2 classElement, String name) {
174174
/// indirect subclasses (ie. subclasses of subclasses).
175175
///
176176
/// Otherwise, returns an empty iterable.
177-
Iterable<ClassElement2> sealedClassImplementations(
177+
Iterable<ClassElement2> sealedSubClasses(
178178
ClassElement2 maybeSealedSuperClass,
179179
) {
180180
if (maybeSealedSuperClass case final sc when sc.isSealed) {

0 commit comments

Comments
 (0)