Skip to content

Commit 07fc93a

Browse files
authored
Better error unknown (#16)
* Improve error tests * Better error handling when hitting unknown fields * Update Dart SDK dependency
1 parent 421c527 commit 07fc93a

6 files changed

+94
-33
lines changed

.travis.yml

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,10 @@ sudo: false
33
dart:
44
- dev
55
- stable
6-
- 1.22.1
76
dart_task:
87
- test
98
- dartfmt
109
- dartanalyzer
11-
matrix:
12-
exclude:
13-
- dart: 1.22.1
14-
dart_task: dartfmt
15-
- dart: 1.22.1
16-
dart_task: dartanalyzer
1710

1811
# Only building master means that we don't run two builds for each pull request.
1912
branches:

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
## 0.1.1
2+
3+
* Fail generation when undefined types are encountered.
4+
Throw a helpful error.
5+
16
## 0.1.0+1
27

38
* Fix homepage in `pubspec.yaml`.

lib/src/json_serializable_generator.dart

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,17 @@ class JsonSerializableGenerator
5858
// TODO: support overriding the field set with an annotation option
5959
var fieldsList = classElement.fields.where((e) => !e.isStatic).toList();
6060

61+
var undefinedFields =
62+
fieldsList.where((fe) => fe.type.isUndefined).toList();
63+
if (undefinedFields.isNotEmpty) {
64+
var description =
65+
undefinedFields.map((fe) => "`${fe.displayName}`").join(', ');
66+
67+
throw new InvalidGenerationSourceError(
68+
'At least one field has an invalid type: $description.',
69+
todo: 'Check names and imports.');
70+
}
71+
6172
// Sort these in the order in which they appear in the class
6273
// Sadly, `classElement.fields` puts properties after fields
6374
fieldsList.sort((a, b) => a.nameOffset.compareTo(b.nameOffset));
@@ -151,6 +162,19 @@ class JsonSerializableGenerator
151162
fieldsToSet.remove(arg.name);
152163
}
153164

165+
var undefinedArgs = [ctorArguments, ctorNamedArguments]
166+
.expand((l) => l)
167+
.where((pe) => pe.type.isUndefined)
168+
.toList();
169+
if (undefinedArgs.isNotEmpty) {
170+
var description =
171+
undefinedArgs.map((fe) => "`${fe.displayName}`").join(', ');
172+
173+
throw new InvalidGenerationSourceError(
174+
'At least one constructor argument has an invalid type: $description.',
175+
todo: 'Check names and imports.');
176+
}
177+
154178
// these are fields to skip – now to find them
155179
var finalFields =
156180
fieldsToSet.values.where((field) => field.isFinal).toSet();
@@ -296,15 +320,13 @@ class JsonSerializableGenerator
296320
{ParameterElement ctorParam}) {
297321
name = _fieldToJsonMapKey(name, field);
298322
var result = "json['$name']";
299-
return _writeAccessToJsonValue(result, field.type, ctorParam: ctorParam);
323+
324+
var targetType = ctorParam?.type ?? field.type;
325+
return _writeAccessToJsonValue(result, targetType);
300326
}
301327

302328
String _writeAccessToJsonValue(String varExpression, DartType searchType,
303-
{ParameterElement ctorParam, int depth: 0}) {
304-
if (ctorParam != null) {
305-
searchType = ctorParam.type as InterfaceType;
306-
}
307-
329+
{int depth: 0}) {
308330
for (var helper in _typeHelpers) {
309331
if (helper.canDeserialize(searchType)) {
310332
return "$varExpression == null ? null : "

pubspec.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
name: json_serializable
2-
version: 0.1.0+1
2+
version: 0.1.1-dev
33
author: Dart Team <misc@dartlang.org>
44
description: Generates utilities to aid in serializing to/from JSON.
55
homepage: https://github.com/dart-lang/json_serializable
66
environment:
7-
sdk: '>=1.22.1 <2.0.0-dev.infinity'
7+
sdk: '>=1.24.0 <2.0.0-dev.infinity'
88
dependencies:
99
analyzer: '>=0.29.10 <0.31.0'
1010
build: ^0.9.0

test/json_serializable_test.dart

Lines changed: 46 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,6 @@ import 'dart:async';
99

1010
import 'package:analyzer/dart/ast/ast.dart';
1111
import 'package:analyzer/dart/element/element.dart';
12-
import 'package:analyzer/src/generated/source.dart';
13-
import 'package:analyzer/src/generated/source_io.dart';
1412
import 'package:analyzer/src/string_source.dart';
1513
import 'package:json_serializable/json_serializable.dart';
1614
import 'package:path/path.dart' as p;
@@ -25,17 +23,43 @@ void main() {
2523
test('const field', () async {
2624
var element = await _getClassForCodeString('theAnswer');
2725

28-
expect(_generator.generate(element, null),
29-
throwsInvalidGenerationSourceError);
30-
// TODO: validate the properties on the thrown error
26+
expect(
27+
_generator.generate(element, null),
28+
throwsInvalidGenerationSourceError(
29+
'Generator cannot target `const dynamic theAnswer`.',
30+
'Remove the JsonSerializable annotation from `const dynamic theAnswer`.'));
3131
});
3232

3333
test('method', () async {
3434
var element = await _getClassForCodeString('annotatedMethod');
3535

36-
expect(_generator.generate(element, null),
37-
throwsInvalidGenerationSourceError);
38-
// TODO: validate the properties on the thrown error
36+
expect(
37+
_generator.generate(element, null),
38+
throwsInvalidGenerationSourceError(
39+
'Generator cannot target `annotatedMethod`.',
40+
'Remove the JsonSerializable annotation from `annotatedMethod`.'));
41+
});
42+
});
43+
44+
group('unknown types', () {
45+
test('in constructor arguments', () async {
46+
var element = await _getClassForCodeString('UnknownCtorParamType');
47+
48+
expect(
49+
_generator.generate(element, null),
50+
throwsInvalidGenerationSourceError(
51+
'At least one constructor argument has an invalid type: `number`.',
52+
'Check names and imports.'));
53+
});
54+
55+
test('in fields', () async {
56+
var element = await _getClassForCodeString('UnknownFieldType');
57+
58+
expect(
59+
_generator.generate(element, null),
60+
throwsInvalidGenerationSourceError(
61+
'At least one field has an invalid type: `number`.',
62+
'Check names and imports.'));
3963
});
4064
});
4165

@@ -126,14 +150,14 @@ Future<Element> _getClassForCodeString(String name) async {
126150
}
127151

128152
Future<CompilationUnit> _getCompilationUnitForString(String projectPath) async {
129-
Source source = new StringSource(_testSource, 'test content');
153+
var source = new StringSource(_testSource, 'test content');
130154

131155
var foundFiles = await getDartFiles(projectPath,
132156
searchList: [p.join(getPackagePath(), 'test', 'test_files')]);
133157

134158
var context = await getAnalysisContextForProjectPath(projectPath, foundFiles);
135159

136-
LibraryElement libElement = context.computeLibraryElement(source);
160+
var libElement = context.computeLibraryElement(source);
137161
return context.resolveCompilationUnit(source, libElement);
138162
}
139163

@@ -207,4 +231,16 @@ class ParentObjectWithDynamicChildren {
207231
String str;
208232
List<dynamic> children;
209233
}
234+
235+
@JsonSerializable()
236+
class UnknownCtorParamType {
237+
int number
238+
239+
UnknownCtorParamType(Bob number) : this.number = number;
240+
}
241+
242+
@JsonSerializable()
243+
class UnknownFieldType {
244+
Bob number
245+
}
210246
''';

test/test_utils.dart

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,17 +27,22 @@ String getPackagePath() {
2727
return _packagePathCache;
2828
}
2929

30-
final Matcher throwsInvalidGenerationSourceError =
31-
throwsA(isInvalidGenerationSourceError);
30+
Matcher throwsInvalidGenerationSourceError(messageMatcher, todoMatcher) =>
31+
throwsA(allOf(
32+
new isInstanceOf<InvalidGenerationSourceError>(),
33+
new FeatureMatcher<InvalidGenerationSourceError>(
34+
'todo', (e) => e.todo, todoMatcher),
35+
new FeatureMatcher<InvalidGenerationSourceError>(
36+
'message', (e) => e.message, messageMatcher)));
3237

33-
const Matcher isInvalidGenerationSourceError =
34-
const _InvalidGenerationSourceError();
38+
// TODO(kevmoo) add this to pkg/matcher – is nice!
39+
class FeatureMatcher<T> extends CustomMatcher {
40+
final dynamic Function(T value) _feature;
3541

36-
class _InvalidGenerationSourceError extends TypeMatcher {
37-
const _InvalidGenerationSourceError() : super("InvalidGenerationSourceError");
42+
FeatureMatcher(String name, this._feature, matcher)
43+
: super("`$name`", "`$name`", matcher);
3844

39-
@override
40-
bool matches(item, Map matchState) => item is InvalidGenerationSourceError;
45+
featureValueOf(covariant T actual) => _feature(actual);
4146
}
4247

4348
/// Returns all of the declarations in [unit], including [unit] as the first

0 commit comments

Comments
 (0)