Fix nullability issues around record types

This commit is contained in:
Simon Binder 2023-01-27 17:12:49 +01:00
parent ba2a0280d3
commit bff8d6c6a1
No known key found for this signature in database
GPG Key ID: 7891917E4147B8C0
4 changed files with 24 additions and 29 deletions

View File

@ -47,6 +47,7 @@ ExistingRowClass? validateExistingClass(
generateInsertable: generateInsertable, generateInsertable: generateInsertable,
knownDriftTypes: knownTypes, knownDriftTypes: knownTypes,
typeProvider: library.typeProvider, typeProvider: library.typeProvider,
typeSystem: library.typeSystem,
); );
} }
@ -238,14 +239,17 @@ ExistingRowClass defaultRecordRowClass({
required List<DriftColumn> columns, required List<DriftColumn> columns,
required bool generateInsertable, required bool generateInsertable,
required TypeProvider typeProvider, required TypeProvider typeProvider,
required TypeSystem typeSystem,
required KnownDriftTypes knownDriftTypes, required KnownDriftTypes knownDriftTypes,
}) { }) {
final type = typeProvider.createRecordType( final type = typeProvider.createRecordType(
positional: const [], positional: const [],
named: [ named: [
for (final column in columns) for (final column in columns)
MapEntry(column.nameInDart, MapEntry(
regularColumnType(typeProvider, knownDriftTypes, column)), column.nameInDart,
regularColumnType(
typeProvider, typeSystem, knownDriftTypes, column)),
], ],
); );
@ -424,7 +428,8 @@ bool checkType(
if (typeConverter != null) { if (typeConverter != null) {
expectedDartType = typeConverter.dartType; expectedDartType = typeConverter.dartType;
if (typeConverter.canBeSkippedForNulls && columnIsNullable) { if (typeConverter.canBeSkippedForNulls && columnIsNullable) {
expectedDartType = typeProvider.makeNullable(expectedDartType); expectedDartType =
typeProvider.makeNullable(expectedDartType, typeSystem);
} }
} else { } else {
expectedDartType = typeProvider.typeFor(columnType, knownTypes); expectedDartType = typeProvider.typeFor(columnType, knownTypes);
@ -440,18 +445,25 @@ bool checkType(
} }
DartType regularColumnType( DartType regularColumnType(
TypeProvider typeProvider, KnownDriftTypes knownTypes, HasType type) { TypeProvider typeProvider,
TypeSystem typeSystem,
KnownDriftTypes knownTypes,
HasType type,
) {
final converter = type.typeConverter; final converter = type.typeConverter;
if (converter != null) { if (converter != null) {
var dartType = converter.dartType; var dartType = converter.dartType;
if (type.nullable && converter.canBeSkippedForNulls) { if (type.nullable && converter.canBeSkippedForNulls) {
return typeProvider.makeNullable(dartType); return typeProvider.makeNullable(dartType, typeSystem);
} else { } else {
return dartType; return dartType;
} }
} else {
final typeForNonNullable = typeProvider.typeFor(type.sqlType, knownTypes);
return type.nullable
? typeProvider.makeNullable(typeForNonNullable, typeSystem)
: typeForNonNullable;
} }
return typeProvider.typeFor(type.sqlType, knownTypes);
} }
extension on TypeProvider { extension on TypeProvider {
@ -499,24 +511,7 @@ extension CreateRecordType on TypeProvider {
); );
} }
DartType makeNullable(DartType type) { DartType makeNullable(DartType type, TypeSystem typeSystem) {
if (type is InterfaceType) { return typeSystem.leastUpperBound(type, nullType);
return type.element.instantiate(
typeArguments: type.typeArguments,
nullabilitySuffix: NullabilitySuffix.question,
);
} else if (type is NeverType) {
return nullType;
} else if (type is RecordType) {
return createRecordType(
positional: [for (final type in type.positionalFields) type.type],
named: [
for (final type in type.namedFields) MapEntry(type.name, type.type),
],
nullabilitySuffix: NullabilitySuffix.question,
);
} else {
return type;
}
} }
} }

View File

@ -578,7 +578,7 @@ class Users extends Table {
.having((e) => e.isRecord, 'isRecord', isTrue) .having((e) => e.isRecord, 'isRecord', isTrue)
.having((e) => e.targetClass, 'targetClass', isNull) .having((e) => e.targetClass, 'targetClass', isNull)
.having((e) => e.targetType.toString(), 'targetType', .having((e) => e.targetType.toString(), 'targetType',
'({int id, String name, DateTime birthday})'), '({DateTime birthday, int id, String name})'),
); );
expect(table.nameOfRowClass, 'User'); expect(table.nameOfRowClass, 'User');
}, skip: requireDart('3.0.0-dev')); }, skip: requireDart('3.0.0-dev'));

View File

@ -148,7 +148,7 @@ CREATE TABLE foo (
.having((e) => e.isRecord, 'isRecord', isTrue) .having((e) => e.isRecord, 'isRecord', isTrue)
.having((e) => e.targetClass, 'targetClass', isNull) .having((e) => e.targetClass, 'targetClass', isNull)
.having((e) => e.targetType.toString(), 'targetType', .having((e) => e.targetType.toString(), 'targetType',
'({String foo, int? bar})'), '({int? bar, String foo})'),
); );
expect(table.nameOfRowClass, 'FooData'); expect(table.nameOfRowClass, 'FooData');
}, skip: requireDart('3.0.0-dev')); }, skip: requireDart('3.0.0-dev'));

View File

@ -73,7 +73,7 @@ class Database {}
checkOutputs({ checkOutputs({
'a|lib/a.drift.dart': decodedMatches(allOf( 'a|lib/a.drift.dart': decodedMatches(allOf(
contains( contains(
'typedef User = ({int id, String name, DateTime? birthDate});', 'typedef User = ({DateTime? birthDate, int id, String name});',
), ),
contains( contains(
'return (id: attachedDatabase.typeMapping.read(DriftSqlType.int, data[\'\${effectivePrefix}id\'])!, ' 'return (id: attachedDatabase.typeMapping.read(DriftSqlType.int, data[\'\${effectivePrefix}id\'])!, '