Don't generate const constr for non-const parent

Closes #2735
This commit is contained in:
Simon Binder 2023-11-19 14:33:55 +01:00
parent 3980fd0be5
commit 39018d381c
No known key found for this signature in database
GPG Key ID: 7891917E4147B8C0
9 changed files with 82 additions and 24 deletions

View File

@ -1,5 +1,8 @@
## 2.14.0-dev ## 2.14.0-dev
- Don't generate `const` row classes when they are extending a class which
isn't const.
## 2.13.2 ## 2.13.2
- Fix generated queries relying on custom types. - Fix generated queries relying on custom types.

View File

@ -198,7 +198,7 @@ extension TypeUtils on DartType {
class DataClassInformation { class DataClassInformation {
final String enforcedName; final String enforcedName;
final AnnotatedDartCode? extending; final CustomParentClass? extending;
final ExistingRowClass? existingClass; final ExistingRowClass? existingClass;
DataClassInformation( DataClassInformation(
@ -234,7 +234,7 @@ class DataClassInformation {
} }
String name; String name;
AnnotatedDartCode? customParentClass; CustomParentClass? customParentClass;
ExistingRowClass? existingClass; ExistingRowClass? existingClass;
if (dataClassName != null) { if (dataClassName != null) {

View File

@ -1,6 +1,7 @@
import 'package:analyzer/dart/constant/value.dart'; import 'package:analyzer/dart/constant/value.dart';
import 'package:analyzer/dart/element/element.dart'; import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/type.dart'; import 'package:analyzer/dart/element/type.dart';
import 'package:drift_dev/src/analysis/results/result_sets.dart';
import '../../driver/error.dart'; import '../../driver/error.dart';
import '../../results/dart.dart'; import '../../results/dart.dart';
@ -24,7 +25,7 @@ String dataClassNameForClassName(String tableName) {
return '${tableName}Data'; return '${tableName}Data';
} }
AnnotatedDartCode? parseCustomParentClass( CustomParentClass? parseCustomParentClass(
String dartTypeName, String dartTypeName,
DartObject dataClassName, DartObject dataClassName,
ClassElement element, ClassElement element,
@ -44,7 +45,6 @@ AnnotatedDartCode? parseCustomParentClass(
'DataClass', 'DataClass',
), ),
); );
return null;
} }
if (extendingType.typeArguments.length > 1) { if (extendingType.typeArguments.length > 1) {
@ -55,7 +55,22 @@ AnnotatedDartCode? parseCustomParentClass(
'type parameter', 'type parameter',
), ),
); );
return null; }
final defaultConstructor =
extendingType.lookUpConstructor(null, element.library);
var isConst = true;
AnnotatedDartCode code;
if (defaultConstructor == null) {
resolver.reportError(
DriftAnalysisError.forDartElement(
element,
'Parameter `extending` in @DataClassName must have an unnamed '
'constructor.',
),
);
} else {
isConst = defaultConstructor.isConst;
} }
// For legacy reasons, if we're extending an existing class with a type // For legacy reasons, if we're extending an existing class with a type
@ -64,7 +79,7 @@ AnnotatedDartCode? parseCustomParentClass(
if (extendingType.typeArguments.length == 1) { if (extendingType.typeArguments.length == 1) {
final genericType = extendingType.typeArguments[0]; final genericType = extendingType.typeArguments[0];
if (genericType.isDartCoreObject || genericType is DynamicType) { if (genericType.isDartCoreObject || genericType is DynamicType) {
return AnnotatedDartCode([ code = AnnotatedDartCode([
DartTopLevelSymbol.topLevelElement(extendingType.element), DartTopLevelSymbol.topLevelElement(extendingType.element),
'<', '<',
DartTopLevelSymbol(dartTypeName, null), DartTopLevelSymbol(dartTypeName, null),
@ -84,7 +99,8 @@ AnnotatedDartCode? parseCustomParentClass(
} }
} }
return AnnotatedDartCode.type(extendingType); code = AnnotatedDartCode.type(extendingType);
return CustomParentClass(parentClass: code, isConst: isConst);
} else { } else {
resolver.reportError( resolver.reportError(
DriftAnalysisError.forDartElement( DriftAnalysisError.forDartElement(

View File

@ -5,6 +5,19 @@ import 'element.dart';
import 'column.dart'; import 'column.dart';
import 'dart.dart'; import 'dart.dart';
/// Drift allows specifying a base class which the generated row class will then
/// extend. This class describes the specified parent class along with
/// additional information needed for code generation.
class CustomParentClass {
final AnnotatedDartCode parentClass;
final bool isConst;
CustomParentClass({
required this.parentClass,
required this.isConst,
});
}
abstract class DriftElementWithResultSet extends DriftSchemaElement { abstract class DriftElementWithResultSet extends DriftSchemaElement {
/// The columns declared in this table or view. /// The columns declared in this table or view.
List<DriftColumn> get columns; List<DriftColumn> get columns;
@ -18,7 +31,7 @@ abstract class DriftElementWithResultSet extends DriftSchemaElement {
ExistingRowClass? get existingRowClass; ExistingRowClass? get existingRowClass;
/// Class that added to data class as implementation /// Class that added to data class as implementation
AnnotatedDartCode? get customParentClass; CustomParentClass? get customParentClass;
/// Whether this table has an existing row class, meaning that drift will not /// Whether this table has an existing row class, meaning that drift will not
/// generate one on its own. /// generate one on its own.

View File

@ -2,7 +2,6 @@ import 'package:collection/collection.dart';
import 'package:drift/drift.dart' show DriftSqlType; import 'package:drift/drift.dart' show DriftSqlType;
import 'package:sqlparser/sqlparser.dart' as sql; import 'package:sqlparser/sqlparser.dart' as sql;
import 'dart.dart';
import 'element.dart'; import 'element.dart';
import 'column.dart'; import 'column.dart';
@ -22,7 +21,7 @@ class DriftTable extends DriftElementWithResultSet {
final ExistingRowClass? existingRowClass; final ExistingRowClass? existingRowClass;
@override @override
final AnnotatedDartCode? customParentClass; final CustomParentClass? customParentClass;
/// The fixed [entityInfoName] to use, overriding the default. /// The fixed [entityInfoName] to use, overriding the default.
final String? fixedEntityInfoName; final String? fixedEntityInfoName;

View File

@ -14,7 +14,7 @@ class DriftView extends DriftElementWithResultSet {
final DriftViewSource source; final DriftViewSource source;
@override @override
final AnnotatedDartCode? customParentClass; final CustomParentClass? customParentClass;
@override @override
String entityInfoName; String entityInfoName;

View File

@ -52,7 +52,8 @@ class ElementSerializer {
for (final constraint in element.tableConstraints) for (final constraint in element.tableConstraints)
_serializeTableConstraint(constraint), _serializeTableConstraint(constraint),
], ],
'custom_parent_class': element.customParentClass?.toJson(), 'custom_parent_class':
_serializeCustomParentClass(element.customParentClass),
'fixed_entity_info_name': element.fixedEntityInfoName, 'fixed_entity_info_name': element.fixedEntityInfoName,
'base_dart_name': element.baseDartName, 'base_dart_name': element.baseDartName,
'row_class_name': element.nameOfRowClass, 'row_class_name': element.nameOfRowClass,
@ -141,7 +142,8 @@ class ElementSerializer {
'existing_data_class': element.existingRowClass != null 'existing_data_class': element.existingRowClass != null
? _serializeExistingRowClass(element.existingRowClass!) ? _serializeExistingRowClass(element.existingRowClass!)
: null, : null,
'custom_parent_class': element.customParentClass?.toJson(), 'custom_parent_class':
_serializeCustomParentClass(element.customParentClass),
'name_of_row_class': element.nameOfRowClass, 'name_of_row_class': element.nameOfRowClass,
'source': serializedSource, 'source': serializedSource,
}; };
@ -310,6 +312,15 @@ class ElementSerializer {
}; };
} }
Map<String, Object?>? _serializeCustomParentClass(CustomParentClass? pc) {
if (pc == null) return null;
return {
'class': pc.parentClass.toJson(),
'const': pc.isConst,
};
}
String? _serializeReferenceAction(ReferenceAction? action) { String? _serializeReferenceAction(ReferenceAction? action) {
return action?.name; return action?.name;
} }
@ -519,9 +530,8 @@ class ElementDeserializer {
for (final constraint in json.list('table_constraints')) for (final constraint in json.list('table_constraints'))
await _readTableConstraint(constraint as Map, columnByName), await _readTableConstraint(constraint as Map, columnByName),
], ],
customParentClass: json['custom_parent_class'] != null customParentClass:
? AnnotatedDartCode.fromJson(json['custom_parent_class'] as Map) _readCustomParentClass(json['custom_parent_class'] as Map?),
: null,
fixedEntityInfoName: json['fixed_entity_info_name'] as String?, fixedEntityInfoName: json['fixed_entity_info_name'] as String?,
baseDartName: json['base_dart_name'] as String, baseDartName: json['base_dart_name'] as String,
nameOfRowClass: json['row_class_name'] as String, nameOfRowClass: json['row_class_name'] as String,
@ -658,9 +668,8 @@ class ElementDeserializer {
references: references, references: references,
columns: columns, columns: columns,
entityInfoName: json['entity_info_name'] as String, entityInfoName: json['entity_info_name'] as String,
customParentClass: json['custom_parent_class'] != null customParentClass:
? AnnotatedDartCode.fromJson(json['custom_parent_class'] as Map) _readCustomParentClass(json['custom_parent_class'] as Map?),
: null,
nameOfRowClass: json['name_of_row_class'] as String, nameOfRowClass: json['name_of_row_class'] as String,
existingRowClass: json['existing_data_class'] != null existingRowClass: json['existing_data_class'] != null
? await _readExistingRowClass( ? await _readExistingRowClass(
@ -805,6 +814,15 @@ class ElementDeserializer {
); );
} }
CustomParentClass? _readCustomParentClass(Map? json) {
if (json == null) return null;
return CustomParentClass(
parentClass: AnnotatedDartCode.fromJson(json['class'] as Map),
isConst: json['const'] as bool,
);
}
ReferenceAction? _readAction(String? value) { ReferenceAction? _readAction(String? value) {
return value == null ? null : ReferenceAction.values.byName(value); return value == null ? null : ReferenceAction.values.byName(value);
} }

View File

@ -44,8 +44,9 @@ class DataClassWriter {
} }
void write() { void write() {
final parentClass = table.customParentClass != null final customParent = table.customParentClass;
? _emitter.dartCode(table.customParentClass!) final parentClass = customParent != null
? _emitter.dartCode(customParent.parentClass)
: _emitter.drift('DataClass'); : _emitter.drift('DataClass');
_buffer.write('class ${table.nameOfRowClass} extends $parentClass '); _buffer.write('class ${table.nameOfRowClass} extends $parentClass ');
@ -76,8 +77,8 @@ class DataClassWriter {
} }
// write constructor with named optional fields // write constructor with named optional fields
if (!scope.options.generateMutableClasses &&
if (!scope.options.generateMutableClasses) { customParent?.isConst != false) {
_buffer.write('const '); _buffer.write('const ');
} }
_emitter _emitter

View File

@ -440,7 +440,7 @@ class FooData {
expect(file.allErrors, isEmpty); expect(file.allErrors, isEmpty);
final table = file.analyzedElements.single as DriftTable; final table = file.analyzedElements.single as DriftTable;
expect(table.customParentClass?.toString(), 'BaseModel'); expect(table.customParentClass?.parentClass.toString(), 'BaseModel');
}); });
test('check valid with type argument', () async { test('check valid with type argument', () async {
@ -473,6 +473,14 @@ class FooData {
'@DataClassName must be subtype of DataClass')) '@DataClassName must be subtype of DataClass'))
], ],
); );
final table = file.analyzedElements.single as DriftTable;
expect(
table.customParentClass,
isA<CustomParentClass>()
.having((e) => e.isConst, 'isConst', false)
.having(
(e) => e.parentClass.toString(), 'parentClass', 'BaseModel'));
}); });
test('wrong type argument in extending', () async { test('wrong type argument in extending', () async {