Skip to content

Migrate to element2 APIs #1504

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions json_serializable/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
## 6.9.6-wip

- Switch to analyzer element2 model and `build: ^3.0.0-dev`.
- Move `package:collection` to a dev dependency.
- Use new `null-aware element` feature in generated code.
- Require Dart 3.8
Expand Down
65 changes: 36 additions & 29 deletions json_serializable/lib/src/decode_helper.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/element2.dart';
import 'package:analyzer/dart/element/nullability_suffix.dart';
import 'package:build/build.dart';
import 'package:source_gen/source_gen.dart';
Expand All @@ -23,7 +23,7 @@ class CreateFactoryResult {

mixin DecodeHelper implements HelperCore {
CreateFactoryResult createFactory(
Map<String, FieldElement> accessibleFields,
Map<String, FieldElement2> accessibleFields,
Map<String, String> unavailableReasons,
) {
assert(config.createFactory);
Expand All @@ -37,14 +37,14 @@ mixin DecodeHelper implements HelperCore {
);

if (config.genericArgumentFactories) {
for (var arg in element.typeParameters) {
for (var arg in element.typeParameters2) {
final helperName = fromJsonForType(
arg.instantiate(nullabilitySuffix: NullabilitySuffix.none),
);

buffer.write(', ${arg.name} Function(Object? json) $helperName');
buffer.write(', ${arg.name3} Function(Object? json) $helperName');
}
if (element.typeParameters.isNotEmpty) {
if (element.typeParameters2.isNotEmpty) {
buffer.write(',');
}
}
Expand All @@ -55,7 +55,7 @@ mixin DecodeHelper implements HelperCore {

String deserializeFun(
String paramOrFieldName, {
ParameterElement? ctorParam,
FormalParameterElement? ctorParam,
}) => _deserializeForField(
accessibleFields[paramOrFieldName]!,
ctorParam: ctorParam,
Expand All @@ -66,21 +66,28 @@ mixin DecodeHelper implements HelperCore {
config.constructor,
accessibleFields.keys,
accessibleFields.values
.where((fe) => element.lookUpSetter(fe.name, element.library) != null)
.map((fe) => fe.name)
.where(
(fe) =>
element.lookUpSetter2(
name: fe.name3!,
library: element.library2,
) !=
null,
)
.map((fe) => fe.name3!)
.toList(),
unavailableReasons,
deserializeFun,
);

final checks = _checkKeys(
accessibleFields.values.where(
(fe) => data.usedCtorParamsAndFields.contains(fe.name),
(fe) => data.usedCtorParamsAndFields.contains(fe.name3),
),
).toList();

if (config.checked) {
final classLiteral = escapeDartString(element.name);
final classLiteral = escapeDartString(element.name3!);

final sectionBuffer = StringBuffer()
..write('''
Expand Down Expand Up @@ -163,11 +170,11 @@ mixin DecodeHelper implements HelperCore {
return CreateFactoryResult(buffer.toString(), data.usedCtorParamsAndFields);
}

Iterable<String> _checkKeys(Iterable<FieldElement> accessibleFields) sync* {
Iterable<String> _checkKeys(Iterable<FieldElement2> accessibleFields) sync* {
final args = <String>[];

String constantList(Iterable<FieldElement> things) =>
'const ${jsonLiteralAsDart(things.map(nameAccess).toList())}';
String constantList(Iterable<FieldElement2> things) =>
'const ${jsonLiteralAsDart(things.map<String>(nameAccess).toList())}';

if (config.disallowUnrecognizedKeys) {
final allowKeysLiteral = constantList(accessibleFields);
Expand Down Expand Up @@ -201,8 +208,8 @@ mixin DecodeHelper implements HelperCore {
/// If [checkedProperty] is `true`, we're using this function to write to a
/// setter.
String _deserializeForField(
FieldElement field, {
ParameterElement? ctorParam,
FieldElement2 field, {
FormalParameterElement? ctorParam,
bool checkedProperty = false,
}) {
final jsonKeyName = safeNameAccess(field);
Expand Down Expand Up @@ -246,7 +253,7 @@ mixin DecodeHelper implements HelperCore {
if (defaultValue != null) {
if (jsonKey.disallowNullValue && jsonKey.required) {
log.warning(
'The `defaultValue` on field `${field.name}` will have no '
'The `defaultValue` on field `${field.name3}` will have no '
'effect because both `disallowNullValue` and `required` are set to '
'`true`.',
);
Expand All @@ -267,30 +274,30 @@ mixin DecodeHelper implements HelperCore {
/// [writableFields] are also populated, but only if they have not already
/// been defined by a constructor parameter with the same name.
_ConstructorData _writeConstructorInvocation(
ClassElement classElement,
ClassElement2 classElement,
String constructorName,
Iterable<String> availableConstructorParameters,
Iterable<String> writableFields,
Map<String, String> unavailableReasons,
String Function(String paramOrFieldName, {ParameterElement ctorParam})
String Function(String paramOrFieldName, {FormalParameterElement ctorParam})
deserializeForField,
) {
final className = classElement.name;
final className = classElement.name3;

final ctor = constructorByName(classElement, constructorName);

final usedCtorParamsAndFields = <String>{};
final constructorArguments = <ParameterElement>[];
final namedConstructorArguments = <ParameterElement>[];
final constructorArguments = <FormalParameterElement>[];
final namedConstructorArguments = <FormalParameterElement>[];

for (final arg in ctor.parameters) {
if (!availableConstructorParameters.contains(arg.name)) {
for (final arg in ctor.formalParameters) {
if (!availableConstructorParameters.contains(arg.name3)) {
if (arg.isRequired) {
var msg =
'Cannot populate the required constructor '
'argument: ${arg.name}.';
'argument: ${arg.name3}.';

final additionalInfo = unavailableReasons[arg.name];
final additionalInfo = unavailableReasons[arg.name3];

if (additionalInfo != null) {
msg = '$msg $additionalInfo';
Expand All @@ -308,7 +315,7 @@ _ConstructorData _writeConstructorInvocation(
} else {
constructorArguments.add(arg);
}
usedCtorParamsAndFields.add(arg.name);
usedCtorParamsAndFields.add(arg.name3!);
}

// fields that aren't already set by the constructor and that aren't final
Expand All @@ -327,7 +334,7 @@ _ConstructorData _writeConstructorInvocation(
..writeAll(
constructorArguments.map((paramElement) {
final content = deserializeForField(
paramElement.name,
paramElement.name3!,
ctorParam: paramElement,
);
return ' $content,\n';
Expand All @@ -336,10 +343,10 @@ _ConstructorData _writeConstructorInvocation(
..writeAll(
namedConstructorArguments.map((paramElement) {
final value = deserializeForField(
paramElement.name,
paramElement.name3!,
ctorParam: paramElement,
);
return ' ${paramElement.name}: $value,\n';
return ' ${paramElement.name3!}: $value,\n';
}),
)
..write(')');
Expand Down
37 changes: 20 additions & 17 deletions json_serializable/lib/src/encoder_helper.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/element2.dart';
import 'package:analyzer/dart/element/nullability_suffix.dart';
import 'package:source_helper/source_helper.dart';

Expand All @@ -13,18 +13,21 @@ import 'type_helpers/json_converter_helper.dart';
import 'unsupported_type_error.dart';

mixin EncodeHelper implements HelperCore {
String _fieldAccess(FieldElement field) => '$_toJsonParamName.${field.name}';
String _fieldAccess(FieldElement2 field) =>
'$_toJsonParamName.${field.name3!}';

String createPerFieldToJson(Set<FieldElement> accessibleFieldSet) {
String createPerFieldToJson(Set<FieldElement2> accessibleFieldSet) {
final buffer = StringBuffer()
..writeln('// ignore: unused_element')
..writeln('abstract class _\$${element.name.nonPrivate}PerFieldToJson {');
..writeln(
'abstract class _\$${element.name3!.nonPrivate}PerFieldToJson {',
);

for (final field in accessibleFieldSet) {
buffer
..writeln(' // ignore: unused_element')
..write(
'static Object? ${field.name}'
'static Object? ${field.name3!}'
'${genericClassArgumentsImpl(withConstraints: true)}'
'(${field.type} $_toJsonParamName',
);
Expand All @@ -43,16 +46,16 @@ mixin EncodeHelper implements HelperCore {

/// Generates an object containing metadatas related to the encoding,
/// destined to be used by other code-generators.
String createFieldMap(Set<FieldElement> accessibleFieldSet) {
String createFieldMap(Set<FieldElement2> accessibleFieldSet) {
assert(config.createFieldMap);

final buffer = StringBuffer(
'const _\$${element.name.nonPrivate}FieldMap = <String, String> {',
'const _\$${element.name3!.nonPrivate}FieldMap = <String, String> {',
);

for (final field in accessibleFieldSet) {
buffer.writeln(
'${escapeDartString(field.name)}: '
'${escapeDartString(field.name3!)}: '
'${escapeDartString(nameAccess(field))},',
);
}
Expand All @@ -64,17 +67,17 @@ mixin EncodeHelper implements HelperCore {

/// Generates an object containing metadatas related to the encoding,
/// destined to be used by other code-generators.
String createJsonKeys(Set<FieldElement> accessibleFieldSet) {
String createJsonKeys(Set<FieldElement2> accessibleFieldSet) {
assert(config.createJsonKeys);

final buffer = StringBuffer(
'abstract final class _\$${element.name.nonPrivate}JsonKeys {',
'abstract final class _\$${element.name3!.nonPrivate}JsonKeys {',
);
// ..write('static const _\$${element.name.nonPrivate}JsonKeys();');

for (final field in accessibleFieldSet) {
buffer.writeln(
'static const String ${field.name} = '
'static const String ${field.name3} = '
'${escapeDartString(nameAccess(field))};',
);
}
Expand All @@ -84,7 +87,7 @@ mixin EncodeHelper implements HelperCore {
return buffer.toString();
}

Iterable<String> createToJson(Set<FieldElement> accessibleFields) sync* {
Iterable<String> createToJson(Set<FieldElement2> accessibleFields) sync* {
assert(config.createToJson);

final buffer = StringBuffer();
Expand Down Expand Up @@ -120,20 +123,20 @@ mixin EncodeHelper implements HelperCore {
}

void _writeGenericArgumentFactories(StringBuffer buffer) {
for (var arg in element.typeParameters) {
for (var arg in element.typeParameters2) {
final helperName = toJsonForType(
arg.instantiate(nullabilitySuffix: NullabilitySuffix.none),
);
buffer.write(',Object? Function(${arg.name} value) $helperName');
buffer.write(',Object? Function(${arg.name3} value) $helperName');
}
if (element.typeParameters.isNotEmpty) {
if (element.typeParameters2.isNotEmpty) {
buffer.write(',');
}
}

static const _toJsonParamName = 'instance';

String _serializeField(FieldElement field, String accessExpression) {
String _serializeField(FieldElement2 field, String accessExpression) {
try {
return getHelperContext(
field,
Expand All @@ -146,7 +149,7 @@ mixin EncodeHelper implements HelperCore {

/// Returns `true` if the field can be written to JSON 'naively' – meaning
/// we can avoid checking for `null`.
bool _canWriteJsonWithoutNullCheck(FieldElement field) {
bool _canWriteJsonWithoutNullCheck(FieldElement2 field) {
final jsonKey = jsonKeyFor(field);

if (jsonKey.includeIfNull) {
Expand Down
22 changes: 11 additions & 11 deletions json_serializable/lib/src/enum_utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// BSD-style license that can be found in the LICENSE file.

import 'package:analyzer/dart/constant/value.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/element2.dart';
import 'package:analyzer/dart/element/type.dart';
import 'package:json_annotation/json_annotation.dart';
import 'package:source_gen/source_gen.dart';
Expand Down Expand Up @@ -45,15 +45,15 @@ String? enumValueMapFromType(
final items = enumMap.entries
.map(
(e) =>
' ${targetType.element!.name}.${e.key.name}: '
' ${targetType.element!.name}.${e.key.name3}: '
'${jsonLiteralAsDart(e.value)},',
)
.join();

return 'const ${constMapName(targetType)} = {\n$items\n};';
}

Map<FieldElement, Object?>? _enumMap(
Map<FieldElement2, Object?>? _enumMap(
DartType targetType, {
bool nullWithNoAnnotation = false,
}) {
Expand All @@ -79,7 +79,7 @@ Map<FieldElement, Object?>? _enumMap(
}

Object? _generateEntry({
required FieldElement field,
required FieldElement2 field,
required JsonEnum jsonEnum,
required DartType targetType,
}) {
Expand All @@ -92,12 +92,12 @@ Object? _generateEntry({
if (valueField != null) {
// TODO: fieldRename is pointless here!!! At least log a warning!

final fieldElementType = field.type.element as EnumElement;
final fieldElementType = field.type.element3 as EnumElement2;

final e = fieldElementType.getField(valueField);
final e = fieldElementType.getField2(valueField);

if (e == null && valueField == 'index') {
return fieldElementType.fields
return fieldElementType.fields2
.where((element) => element.isEnumConstant)
.toList(growable: false)
.indexOf(field);
Expand All @@ -108,7 +108,7 @@ Object? _generateEntry({
'`JsonEnum.valueField` was set to "$valueField", but '
'that is not a valid, instance field on '
'`${typeToCode(targetType)}`.',
element: targetType.element,
element: targetType.element3,
);
}

Expand All @@ -120,11 +120,11 @@ Object? _generateEntry({
throw InvalidGenerationSourceError(
'`JsonEnum.valueField` was set to "$valueField", but '
'that field does not have a type of String, int, or null.',
element: targetType.element,
element: targetType.element3,
);
}
} else {
return encodedFieldName(jsonEnum.fieldRename, field.name);
return encodedFieldName(jsonEnum.fieldRename, field.name3!);
}
} else {
final reader = ConstantReader(annotation);
Expand All @@ -136,7 +136,7 @@ Object? _generateEntry({
} else {
final targetTypeCode = typeToCode(targetType);
throw InvalidGenerationSourceError(
'The `JsonValue` annotation on `$targetTypeCode.${field.name}` does '
'The `JsonValue` annotation on `$targetTypeCode.${field.name3}` does '
'not have a value of type String, int, or null.',
element: field,
);
Expand Down
Loading
Loading