mirror of https://github.com/AMT-Cheif/drift.git
Check types for existing row classes
This commit is contained in:
parent
885c63e66e
commit
8b5d5a9f6c
|
@ -1,5 +1,6 @@
|
|||
// @dart=2.9
|
||||
import 'package:analyzer/dart/element/element.dart';
|
||||
import 'package:analyzer/dart/element/type.dart';
|
||||
import 'package:moor_generator/moor_generator.dart';
|
||||
import 'package:moor_generator/src/analyzer/errors.dart';
|
||||
|
||||
|
@ -24,6 +25,7 @@ ExistingRowClass /*?*/ validateExistingClass(
|
|||
final column = unmatchedColumnsByName.remove(parameter.name);
|
||||
if (column != null) {
|
||||
columnsToParameter[column] = parameter;
|
||||
_checkType(parameter, column, errors);
|
||||
} else if (!parameter.isOptional) {
|
||||
errors.report(ErrorInDartCode(
|
||||
affectedElement: parameter,
|
||||
|
@ -35,3 +37,58 @@ ExistingRowClass /*?*/ validateExistingClass(
|
|||
|
||||
return ExistingRowClass(desiredClass, ctor, columnsToParameter);
|
||||
}
|
||||
|
||||
void _checkType(ParameterElement element, MoorColumn column, ErrorSink errors) {
|
||||
final type = element.type;
|
||||
final typesystem = element.library.typeSystem;
|
||||
|
||||
void error(String message) {
|
||||
errors.report(ErrorInDartCode(
|
||||
affectedElement: element,
|
||||
message: message,
|
||||
));
|
||||
}
|
||||
|
||||
if (element.library.isNonNullableByDefault &&
|
||||
column.nullableInDart &&
|
||||
!typesystem.isNullable(type) &&
|
||||
element.isNotOptional) {
|
||||
error('Expected this parameter to be nullable');
|
||||
return;
|
||||
}
|
||||
|
||||
// If there's a type converter, ensure the type matches
|
||||
if (column.typeConverter != null) {
|
||||
final mappedType = column.typeConverter.mappedType;
|
||||
if (!typesystem.isAssignableTo(mappedType, type)) {
|
||||
error('Parameter must accept '
|
||||
'${mappedType.getDisplayString(withNullability: true)}');
|
||||
}
|
||||
} else {
|
||||
// No type converter, check raw column type
|
||||
if (!type.matches(column.type)) {
|
||||
error('Invalid type, expected ${dartTypeNames[column.type]}');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension on DartType {
|
||||
bool matches(ColumnType type) {
|
||||
switch (type) {
|
||||
case ColumnType.integer:
|
||||
return isDartCoreInt;
|
||||
case ColumnType.text:
|
||||
return isDartCoreString;
|
||||
case ColumnType.boolean:
|
||||
return isDartCoreBool;
|
||||
case ColumnType.real:
|
||||
return isDartCoreDouble;
|
||||
case ColumnType.datetime:
|
||||
return element.name == 'DateTime' && element.library.isDartCore;
|
||||
case ColumnType.blob:
|
||||
return isDartCoreList;
|
||||
}
|
||||
|
||||
throw AssertionError('Unhandled moor type');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,111 @@
|
|||
// @dart=2.9
|
||||
@Tags(['analyzer'])
|
||||
import 'package:moor_generator/src/analyzer/errors.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import '../utils.dart';
|
||||
|
||||
void main() {
|
||||
TestState state;
|
||||
|
||||
setUpAll(() {
|
||||
state = TestState.withContent({
|
||||
'a|lib/invalid_no_unnamed_constructor.dart': '''
|
||||
import 'package:moor/moor.dart';
|
||||
|
||||
class RowClass {
|
||||
RowClass.create();
|
||||
}
|
||||
@UseRowClass(RowClass)
|
||||
class TableClass extends Table {}
|
||||
''',
|
||||
'a|lib/mismatching_type.dart': '''
|
||||
import 'package:moor/moor.dart';
|
||||
|
||||
class RowClass {
|
||||
RowClass(int x);
|
||||
}
|
||||
@UseRowClass(RowClass)
|
||||
class TableClass extends Table {
|
||||
TextColumn get x => text()();
|
||||
}
|
||||
''',
|
||||
'a|lib/mismatching_nullability.dart': '''
|
||||
import 'package:moor/moor.dart';
|
||||
|
||||
class RowClass {
|
||||
RowClass(int x);
|
||||
}
|
||||
@UseRowClass(RowClass)
|
||||
class TableClass extends Table {
|
||||
IntColumn get x => integer().nullable()();
|
||||
}
|
||||
''',
|
||||
'a|lib/mismatching_type_converter.dart': '''
|
||||
import 'package:moor/moor.dart';
|
||||
|
||||
class MyConverter extends TypeConverter<int, String> {
|
||||
const MyConverter();
|
||||
|
||||
@override
|
||||
int? mapToDart(String? fromDb) => throw 'stub';
|
||||
@override
|
||||
String? mapToSql(int? value) => throw 'stub';
|
||||
}
|
||||
|
||||
class RowClass {
|
||||
RowClass(String x);
|
||||
}
|
||||
|
||||
@UseRowClass(RowClass)
|
||||
class TableClass extends Table {
|
||||
TextColumn get x => text().map(const MyConverter())();
|
||||
}
|
||||
''',
|
||||
});
|
||||
});
|
||||
|
||||
tearDownAll(() => state.close());
|
||||
|
||||
group('warns about misuse', () {
|
||||
test('when the desired row class does not have an unnamed constructor',
|
||||
() async {
|
||||
final file =
|
||||
await state.analyze('package:a/invalid_no_unnamed_constructor.dart');
|
||||
expect(
|
||||
file.errors.errors,
|
||||
contains(isA<ErrorInDartCode>().having((e) => e.message, 'message',
|
||||
contains('must have an unnamed constructor'))),
|
||||
);
|
||||
});
|
||||
|
||||
test('when a parameter has a mismatching type', () async {
|
||||
final file = await state.analyze('package:a/mismatching_type.dart');
|
||||
expect(
|
||||
file.errors.errors,
|
||||
contains(isA<ErrorInDartCode>().having((e) => e.message, 'message',
|
||||
contains('Invalid type, expected String'))),
|
||||
);
|
||||
});
|
||||
|
||||
test('when a parameter should be nullable', () async {
|
||||
final file =
|
||||
await state.analyze('package:a/mismatching_nullability.dart');
|
||||
expect(
|
||||
file.errors.errors,
|
||||
contains(isA<ErrorInDartCode>().having((e) => e.message, 'message',
|
||||
'Expected this parameter to be nullable')),
|
||||
);
|
||||
});
|
||||
|
||||
test('when a parameter has a mismatching type converter', () async {
|
||||
final file =
|
||||
await state.analyze('package:a/mismatching_type_converter.dart');
|
||||
expect(
|
||||
file.errors.errors,
|
||||
contains(isA<ErrorInDartCode>()
|
||||
.having((e) => e.message, 'message', 'Parameter must accept int')),
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
Loading…
Reference in New Issue