Skip to content

Commit

Permalink
Merge pull request #427 from davidmorgan/fix-generic-inherit
Browse files Browse the repository at this point in the history
Fix code generation when inherited generic fields are made non-generic.
  • Loading branch information
davidmorgan authored May 29, 2018
2 parents 556c70d + 7a2f6ab commit 93c2acb
Show file tree
Hide file tree
Showing 16 changed files with 218 additions and 18 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

# 5.4.6

- Fix code generation when inherited generic fields are made non-generic.

# 5.4.5

- Improve error message on failure to deserialize.
Expand Down
34 changes: 26 additions & 8 deletions built_value_generator/lib/src/fields.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,33 @@
// license that can be found in the LICENSE file.

import 'dart:collection';

import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/type.dart';
import 'package:built_collection/built_collection.dart';

/// Gets fields, including from interfaces. Fields from interfaces are only
/// returned if they are not also implemented by a mixin.
///
/// If a field is overriden then just the closest (overriding) field is
/// If a field is overridden then just the closest (overriding) field is
/// returned.
BuiltList<FieldElement> collectFields(ClassElement element) =>
collectFieldsForType(element.type);

/// Gets fields, including from interfaces. Fields from interfaces are only
/// returned if they are not also implemented by a mixin.
///
/// If a field is overridden then just the closest (overriding) field is
/// returned.
BuiltList<FieldElement> collectFields(ClassElement element) {
BuiltList<FieldElement> collectFieldsForType(InterfaceType type) {
final fields = <FieldElement>[];
// Add fields from this class before interfaces, so they're added to the set
// first below. Re-added fields from interfaces are ignored.
fields.addAll(element.fields);
fields.addAll(_fieldElementsForType(type));

new Set<InterfaceType>.from(element.interfaces)
..addAll(element.mixins)
..forEach((interface) => fields.addAll(collectFields(interface.element)));
new Set<InterfaceType>.from(type.interfaces)
..addAll(type.mixins)
..forEach((interface) => fields.addAll(collectFieldsForType(interface)));

// Overridden fields have multiple declarations, so deduplicate by adding
// to a set that compares on field name.
Expand All @@ -33,6 +42,15 @@ BuiltList<FieldElement> collectFields(ClassElement element) {
return new BuiltList<FieldElement>.build((b) => b
..addAll(fieldSet)
..where((field) =>
element.lookUpInheritedConcreteGetter(field.name, element.library) ==
null));
type.lookUpInheritedGetter(field.name, thisType: false)?.isAbstract ??
true));
}

BuiltList<FieldElement> _fieldElementsForType(InterfaceType type) {
final result = new ListBuilder<FieldElement>();
for (final accessor in type.accessors) {
if (accessor.isSetter) continue;
result.add(accessor.variable as FieldElement);
}
return result.build();
}
22 changes: 17 additions & 5 deletions built_value_generator/lib/src/serializer_source_field.dart
Original file line number Diff line number Diff line change
Expand Up @@ -71,11 +71,23 @@ abstract class SerializerSourceField

/// The [type] plus any import prefix.
@memoized
String get typeWithPrefix =>
(element.getter.computeNode() as MethodDeclaration)
?.returnType
?.toString() ??
'dynamic';
String get typeWithPrefix {
final typeFromAst = (element.getter.computeNode() as MethodDeclaration)
?.returnType
?.toString() ??
'dynamic';
final typeFromElement = type;

// If the type is a function, we can't use the element result; it is
// formatted incorrectly.
if (typeFromElement.contains('(')) return typeFromAst;

// If the type does not have an import prefix, prefer the element result.
// It handles inherited generics correctly.
if (!typeFromAst.contains('.')) return typeFromElement;

return typeFromAst;
}

/// Returns the type with import prefix if the compilation unit matches,
/// otherwise the type with no import prefix.
Expand Down
22 changes: 17 additions & 5 deletions built_value_generator/lib/src/value_source_field.dart
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,23 @@ abstract class ValueSourceField

/// The [type] plus any import prefix.
@memoized
String get typeWithPrefix =>
(element.getter.computeNode() as MethodDeclaration)
?.returnType
?.toString() ??
'dynamic';
String get typeWithPrefix {
final typeFromAst = (element.getter.computeNode() as MethodDeclaration)
?.returnType
?.toString() ??
'dynamic';
final typeFromElement = type;

// If the type is a function, we can't use the element result; it is
// formatted incorrectly.
if (typeFromElement.contains('(')) return typeFromAst;

// If the type does not have an import prefix, prefer the element result.
// It handles inherited generics correctly.
if (!typeFromAst.contains('.')) return typeFromElement;

return typeFromAst;
}

/// Returns the type with import prefix if the compilation unit matches,
/// otherwise the type with no import prefix.
Expand Down
14 changes: 14 additions & 0 deletions end_to_end_test/lib/generics.dart
Original file line number Diff line number Diff line change
Expand Up @@ -95,3 +95,17 @@ abstract class CustomBuilderGenericValueBuilder<T>
_$CustomBuilderGenericValueBuilder<T>;
CustomBuilderGenericValueBuilder._();
}

abstract class Generic<T> {
T get value;
}

abstract class ConcreteGeneric
implements Built<ConcreteGeneric, ConcreteGenericBuilder>, Generic<int> {
static Serializer<ConcreteGeneric> get serializer =>
_$concreteGenericSerializer;

factory ConcreteGeneric([updates(ConcreteGenericBuilder b)]) =
_$ConcreteGeneric;
ConcreteGeneric._();
}
118 changes: 118 additions & 0 deletions end_to_end_test/lib/generics.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions end_to_end_test/lib/serializers.dart
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ part 'serializers.g.dart';
CompoundValue,
CompoundValueExplicitNoNesting,
CompoundValueNoNesting,
ConcreteGeneric,
EnumWithInt,
FieldDiscoveryValue,
Fish,
Expand Down
1 change: 1 addition & 0 deletions end_to_end_test/lib/serializers.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 13 additions & 0 deletions end_to_end_test/test/generics_serializer_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -372,4 +372,17 @@ void main() {
data);
});
});

group('ConcreteGeneric with unknown specifiedType', () {
final data = new ConcreteGeneric((b) => b..value = 1);
final serialized = ['ConcreteGeneric', 'value', 1];

test('can be serialized', () {
expect(serializers.serialize(data), serialized);
});

test('can be deserialized', () {
expect(serializers.deserialize(serialized), data);
});
});
}
1 change: 1 addition & 0 deletions example/lib/collections.g.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// This is generated code; please do not modify by hand.

part of collections;

// **************************************************************************
Expand Down
1 change: 1 addition & 0 deletions example/lib/enums.g.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// This is generated code; please do not modify by hand.

part of enums;

// **************************************************************************
Expand Down
1 change: 1 addition & 0 deletions example/lib/generics.g.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// This is generated code; please do not modify by hand.

part of generics;

// **************************************************************************
Expand Down
1 change: 1 addition & 0 deletions example/lib/interfaces.g.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// This is generated code; please do not modify by hand.

part of interfaces;

// **************************************************************************
Expand Down
1 change: 1 addition & 0 deletions example/lib/polymorphism.g.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// This is generated code; please do not modify by hand.

part of polymorphism;

// **************************************************************************
Expand Down
1 change: 1 addition & 0 deletions example/lib/serializers.g.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// This is generated code; please do not modify by hand.

part of serializers;

// **************************************************************************
Expand Down
1 change: 1 addition & 0 deletions example/lib/values.g.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// This is generated code; please do not modify by hand.

part of values;

// **************************************************************************
Expand Down

0 comments on commit 93c2acb

Please sign in to comment.