From 02653f91b22b3f0a44f4d75ffaed0ec5dafaaec5 Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Thu, 10 Sep 2020 12:55:19 -0700 Subject: [PATCH] Improve error message when @JsonSerializable is used with generics Follow-up on https://github.com/google/json_serializable.dart/pull/714 https://github.com/google/json_serializable.dart/pull/714/commits/ae72259af39c68c57657934071077b868fe256e3 Also drop use of InvalidGenerationSourceError.todo See https://github.com/dart-lang/source_gen/issues/480 --- json_serializable/lib/src/helper_core.dart | 29 +++++++++++++++---- .../lib/src/json_serializable_generator.dart | 14 ++++----- .../src/_json_serializable_test_input.dart | 12 +++----- .../test/src/core_subclass_type_input.dart | 6 ---- .../test/src/generic_test_input.dart | 15 ++++++++-- 5 files changed, 46 insertions(+), 30 deletions(-) diff --git a/json_serializable/lib/src/helper_core.dart b/json_serializable/lib/src/helper_core.dart index bf99f6962..34d7fd7a0 100644 --- a/json_serializable/lib/src/helper_core.dart +++ b/json_serializable/lib/src/helper_core.dart @@ -60,14 +60,33 @@ InvalidGenerationSourceError createInvalidGenerationError( UnsupportedTypeError e, ) { var message = 'Could not generate `$targetMember` code for `${field.name}`'; - if (field.type != e.type) { + String todo; + + if (e.type is TypeParameterType) { + message = '$message because of type `${e.type.getDisplayString()}` ' + '(type parameter)'; + + todo = r''' +To support type paramaters (generic types) you can: +1) Use `JsonConverter` + https://pub.dev/documentation/json_annotation/latest/json_annotation/JsonConverter-class.html +2) Use `JsonKey` fields `fromJson` and `toJson` + https://pub.dev/documentation/json_annotation/latest/json_annotation/JsonKey/fromJson.html + https://pub.dev/documentation/json_annotation/latest/json_annotation/JsonKey/toJson.html +3) Set `JsonSerializable.genericArgumentFactories` to `true` + https://pub.dev/documentation/json_annotation/latest/json_annotation/JsonSerializable/genericArgumentFactories.html'''; + } else if (field.type != e.type) { message = '$message because of type `${typeToCode(e.type)}`'; } - message = '$message.\n${e.reason}'; + + final messageItems = [ + '$message.', + e.reason, + if (todo != null) todo, + ]; return InvalidGenerationSourceError( - message, - todo: 'Make sure all of the types are serializable.', + messageItems.join('\n'), element: field, ); } @@ -126,8 +145,6 @@ String typeToCode(DartType type) { final typeArgumentsCode = typeArguments.map(typeToCode).join(', '); return '${type.element.name}<$typeArgumentsCode>'; } - } else if (type is TypeParameterType) { - return '${type.getDisplayString()} (type parameter)'; } throw UnimplementedError('(${type.runtimeType}) $type'); } diff --git a/json_serializable/lib/src/json_serializable_generator.dart b/json_serializable/lib/src/json_serializable_generator.dart index 388c0486e..84789ab87 100644 --- a/json_serializable/lib/src/json_serializable_generator.dart +++ b/json_serializable/lib/src/json_serializable_generator.dart @@ -88,10 +88,10 @@ class JsonSerializableGenerator BuildStep buildStep, ) { if (element is! ClassElement) { - final name = element.name; - throw InvalidGenerationSourceError('Generator cannot target `$name`.', - todo: 'Remove the JsonSerializable annotation from `$name`.', - element: element); + throw InvalidGenerationSourceError( + '`@JsonSerializable` can only be used on classes.', + element: element, + ); } final classElement = element as ClassElement; @@ -186,9 +186,9 @@ class _GeneratorHelper extends HelperCore with EncodeHelper, DecodeHelper { final jsonKey = nameAccess(fe); if (!set.add(jsonKey)) { throw InvalidGenerationSourceError( - 'More than one field has the JSON key `$jsonKey`.', - todo: 'Check the `JsonKey` annotations on fields.', - element: fe); + 'More than one field has the JSON key for name "$jsonKey".', + element: fe, + ); } return set; }, diff --git a/json_serializable/test/src/_json_serializable_test_input.dart b/json_serializable/test/src/_json_serializable_test_input.dart index a03dcacdf..441e399bc 100644 --- a/json_serializable/test/src/_json_serializable_test_input.dart +++ b/json_serializable/test/src/_json_serializable_test_input.dart @@ -29,13 +29,11 @@ part 'to_from_json_test_input.dart'; part 'unknown_enum_value_test_input.dart'; -@ShouldThrow('Generator cannot target `theAnswer`.', - todo: 'Remove the JsonSerializable annotation from `theAnswer`.') +@ShouldThrow('`@JsonSerializable` can only be used on classes.') @JsonSerializable() const theAnswer = 42; -@ShouldThrow('Generator cannot target `annotatedMethod`.', - todo: 'Remove the JsonSerializable annotation from `annotatedMethod`.') +@ShouldThrow('`@JsonSerializable` can only be used on classes.') @JsonSerializable() Object annotatedMethod() => null; @@ -271,8 +269,7 @@ class NoCtorClass { } @ShouldThrow( - 'More than one field has the JSON key `str`.', - todo: 'Check the `JsonKey` annotations on fields.', + 'More than one field has the JSON key for name "str".', element: 'str', ) @JsonSerializable(createFactory: false) @@ -284,8 +281,7 @@ class KeyDupesField { } @ShouldThrow( - 'More than one field has the JSON key `a`.', - todo: 'Check the `JsonKey` annotations on fields.', + 'More than one field has the JSON key for name "a".', element: 'str', ) @JsonSerializable(createFactory: false) diff --git a/json_serializable/test/src/core_subclass_type_input.dart b/json_serializable/test/src/core_subclass_type_input.dart index cb5d91cf3..1e796e703 100644 --- a/json_serializable/test/src/core_subclass_type_input.dart +++ b/json_serializable/test/src/core_subclass_type_input.dart @@ -4,7 +4,6 @@ part of '_json_serializable_test_input.dart'; r''' Could not generate `fromJson` code for `mapView`. None of the provided `TypeHelper` instances support the defined type.''', - todo: 'Make sure all of the types are serializable.', element: 'mapView', ) @JsonSerializable(createToJson: false) @@ -16,7 +15,6 @@ class UnsupportedMapField { r''' Could not generate `fromJson` code for `listView`. None of the provided `TypeHelper` instances support the defined type.''', - todo: 'Make sure all of the types are serializable.', element: 'listView', ) @JsonSerializable(createToJson: false) @@ -28,7 +26,6 @@ class UnsupportedListField { r''' Could not generate `fromJson` code for `customSet`. None of the provided `TypeHelper` instances support the defined type.''', - todo: 'Make sure all of the types are serializable.', element: 'customSet', ) @JsonSerializable(createToJson: false) @@ -42,7 +39,6 @@ abstract class _CustomSet implements Set {} r''' Could not generate `fromJson` code for `customDuration`. None of the provided `TypeHelper` instances support the defined type.''', - todo: 'Make sure all of the types are serializable.', element: 'customDuration', ) @JsonSerializable(createToJson: false) @@ -56,7 +52,6 @@ abstract class _CustomDuration implements Duration {} r''' Could not generate `fromJson` code for `customUri`. None of the provided `TypeHelper` instances support the defined type.''', - todo: 'Make sure all of the types are serializable.', element: 'customUri', ) @JsonSerializable(createToJson: false) @@ -70,7 +65,6 @@ abstract class _CustomUri implements Uri {} r''' Could not generate `fromJson` code for `customDateTime`. None of the provided `TypeHelper` instances support the defined type.''', - todo: 'Make sure all of the types are serializable.', element: 'customDateTime', ) @JsonSerializable(createToJson: false) diff --git a/json_serializable/test/src/generic_test_input.dart b/json_serializable/test/src/generic_test_input.dart index eeeaaf832..cfcef86f1 100644 --- a/json_serializable/test/src/generic_test_input.dart +++ b/json_serializable/test/src/generic_test_input.dart @@ -5,9 +5,18 @@ part of '_json_serializable_test_input.dart'; @ShouldThrow( - 'Could not generate `fromJson` code for `result` because of type ' - '`TResult (type parameter)`.\n' - 'None of the provided `TypeHelper` instances support the defined type.', + r''' +Could not generate `fromJson` code for `result` because of type `TResult` (type parameter). +None of the provided `TypeHelper` instances support the defined type. +To support type paramaters (generic types) you can: +1) Use `JsonConverter` + https://pub.dev/documentation/json_annotation/latest/json_annotation/JsonConverter-class.html +2) Use `JsonKey` fields `fromJson` and `toJson` + https://pub.dev/documentation/json_annotation/latest/json_annotation/JsonKey/fromJson.html + https://pub.dev/documentation/json_annotation/latest/json_annotation/JsonKey/toJson.html +3) Set `JsonSerializable.genericArgumentFactories` to `true` + https://pub.dev/documentation/json_annotation/latest/json_annotation/JsonSerializable/genericArgumentFactories.html''', + element: 'result', ) @JsonSerializable() class Issue713 {