Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
6 changes: 3 additions & 3 deletions packages/freezed/build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ targets:
enabled: true
generate_for:
exclude:
- test
- example
- test/source_gen_src.dart
include:
- test/integration/*
- test/integration/**/*
- test/*
- test/**/*
source_gen|combining_builder:
options:
ignore_for_file:
Expand Down
4 changes: 3 additions & 1 deletion packages/freezed/lib/src/ast.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/ast/token.dart';

extension AstX on AstNode {
String get documentation {
String? get documentation {
final builder = StringBuffer();

for (Token? token = beginToken.precedingComments;
Expand All @@ -11,6 +11,8 @@ extension AstX on AstNode {
builder.writeln(token);
}

if (builder.isEmpty) return null;

return builder.toString();
}
}
Expand Down
165 changes: 12 additions & 153 deletions packages/freezed/lib/src/freezed_generator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/constant/value.dart';
import 'package:collection/collection.dart';
import 'package:freezed/src/templates/copy_with.dart';
import 'package:freezed/src/templates/properties.dart';
import 'package:freezed/src/tools/type.dart';
import 'package:freezed_annotation/freezed_annotation.dart' show Freezed;
import 'package:meta/meta.dart';
import 'package:source_gen/source_gen.dart';
Expand All @@ -21,14 +19,6 @@ extension StringX on String {
}
}

class CommonProperties {
/// Properties that have a getter in the abstract class
final List<Property> readableProperties = [];

/// Properties that are visible on `copyWith`
final List<Property> cloneableProperties = [];
}

@immutable
class FreezedGenerator extends ParserGenerator<Freezed> {
FreezedGenerator(this._buildYamlConfigs);
Expand Down Expand Up @@ -57,141 +47,14 @@ class FreezedGenerator extends ParserGenerator<Freezed> {
return Class.from(declaration, configs, globalConfigs: _buildYamlConfigs);
}

CommonProperties _commonParametersBetweenAllConstructors(Class data) {
final constructorsNeedsGeneration = data.constructors;

final result = CommonProperties();
if (constructorsNeedsGeneration case [final ctor]) {
result.cloneableProperties.addAll(
constructorsNeedsGeneration.first.parameters.allParameters
.map(Property.fromParameter),
);
result.readableProperties.addAll(result.cloneableProperties
.where((p) => ctor.isSynthetic(param: p.name)));
return result;
}

parameterLoop:
for (final parameter
in constructorsNeedsGeneration.first.parameters.allParameters) {
final isSynthetic =
constructorsNeedsGeneration.first.isSynthetic(param: parameter.name);

final library = parameter.parameterElement!.library!;

var commonTypeBetweenAllUnionConstructors =
parameter.parameterElement!.type;

for (final constructor in constructorsNeedsGeneration) {
final matchingParameter = constructor.parameters.allParameters
.firstWhereOrNull((p) => p.name == parameter.name);
// The property is not present in one of the union cases, so shouldn't
// be present in the abstract class.
if (matchingParameter == null) continue parameterLoop;

commonTypeBetweenAllUnionConstructors =
library.typeSystem.leastUpperBound(
commonTypeBetweenAllUnionConstructors,
matchingParameter.parameterElement!.type,
);
}

final matchingParameters = constructorsNeedsGeneration
.expand((element) => element.parameters.allParameters)
.where((element) => element.name == parameter.name)
.toList();

final isFinal = matchingParameters.any(
(element) =>
element.isFinal ||
element.parameterElement?.type !=
commonTypeBetweenAllUnionConstructors,
);

final nonNullableCommonType = library.typeSystem
.promoteToNonNull(commonTypeBetweenAllUnionConstructors);

final didDowncast = matchingParameters.any(
(element) =>
element.parameterElement?.type !=
commonTypeBetweenAllUnionConstructors,
);
final didNonNullDowncast = matchingParameters.any(
(element) =>
element.parameterElement?.type !=
commonTypeBetweenAllUnionConstructors &&
element.parameterElement?.type != nonNullableCommonType,
);
final didNullDowncast = !didNonNullDowncast && didDowncast;

final commonTypeString = resolveFullTypeStringFrom(
library,
commonTypeBetweenAllUnionConstructors,
);

final commonProperty = Property(
isFinal: isFinal,
type: commonTypeString,
isNullable: commonTypeBetweenAllUnionConstructors.isNullable,
isDartList: commonTypeBetweenAllUnionConstructors.isDartCoreList,
isDartMap: commonTypeBetweenAllUnionConstructors.isDartCoreMap,
isDartSet: commonTypeBetweenAllUnionConstructors.isDartCoreSet,
isPossiblyDartCollection:
commonTypeBetweenAllUnionConstructors.isPossiblyDartCollection,
name: parameter.name,
decorators: parameter.decorators,
defaultValueSource: parameter.defaultValueSource,
doc: parameter.doc,
// TODO support JsonKey
hasJsonKey: false,
);

if (isSynthetic) result.readableProperties.add(commonProperty);

// For {int a, int b, int c} | {int a, int? b, double c}, allows:
// copyWith({int a, int b})
// - int? b is not allowed because `null` is not compatible with the
// first union case.
// - num c is not allowed because num is not assignable int/double
if (!didNonNullDowncast) {
final copyWithType = didNullDowncast
? nonNullableCommonType
: commonTypeBetweenAllUnionConstructors;

result.cloneableProperties.add(
Property(
isFinal: isFinal,
type: resolveFullTypeStringFrom(
library,
copyWithType,
),
isNullable: copyWithType.isNullable,
isDartList: copyWithType.isDartCoreList,
isDartMap: copyWithType.isDartCoreMap,
isDartSet: copyWithType.isDartCoreSet,
isPossiblyDartCollection: copyWithType.isPossiblyDartCollection,
name: parameter.name,
decorators: parameter.decorators,
defaultValueSource: parameter.defaultValueSource,
doc: parameter.doc,
// TODO support JsonKey
hasJsonKey: false,
),
);
}
}

return result;
}

Iterable<DeepCloneableProperty> _getCommonDeepCloneableProperties(
List<ConstructorDetails> constructors,
CommonProperties commonProperties,
PropertyList commonProperties,
) sync* {
for (final commonProperty in commonProperties.cloneableProperties) {
final commonGetter = commonProperties.readableProperties
.firstWhereOrNull((e) => e.name == commonProperty.name);
final deepCopyProperty = constructors.first.deepCloneableProperties
final deepCopyProperty = constructors.firstOrNull?.deepCloneableProperties
.firstWhereOrNull((e) => e.name == commonProperty.name);

if (deepCopyProperty == null || commonGetter == null) continue;
Expand Down Expand Up @@ -239,17 +102,15 @@ class FreezedGenerator extends ParserGenerator<Freezed> {
) sync* {
if (data.options.fromJson) yield FromJson(data);

final commonProperties = _commonParametersBetweenAllConstructors(data);

final commonCopyWith = data.options.annotation.copyWith ??
commonProperties.cloneableProperties.isNotEmpty
data.properties.cloneableProperties.isNotEmpty
? CopyWith(
clonedClassName: data.name,
readableProperties: commonProperties.readableProperties,
cloneableProperties: commonProperties.cloneableProperties,
readableProperties: data.properties.readableProperties,
cloneableProperties: data.properties.cloneableProperties,
deepCloneableProperties: _getCommonDeepCloneableProperties(
data.constructors,
commonProperties,
data.properties,
).toList(),
genericsDefinition: data.genericsDefinitionTemplate,
genericsParameter: data.genericsParameterTemplate,
Expand All @@ -260,25 +121,23 @@ class FreezedGenerator extends ParserGenerator<Freezed> {
yield Abstract(
data: data,
copyWith: commonCopyWith,
commonProperties: commonProperties.readableProperties,
commonProperties: data.properties.readableProperties,
globalData: globalData,
);

for (final constructor in data.constructors) {
yield Concrete(
data: data,
constructor: constructor,
commonProperties: commonProperties.readableProperties,
commonProperties: data.properties.readableProperties,
globalData: globalData,
copyWith: data.options.annotation.copyWith ??
constructor.parameters.allParameters.isNotEmpty
? CopyWith(
clonedClassName: constructor.redirectedName,
cloneableProperties:
constructor.properties.map((e) => e.value).toList(),
readableProperties: constructor.properties
.where((e) => e.isSynthetic)
.map((e) => e.value)
.toList(),
cloneableProperties: constructor.properties.toList(),
readableProperties:
constructor.properties.where((e) => e.isSynthetic).toList(),
deepCloneableProperties: constructor.deepCloneableProperties,
genericsDefinition: data.genericsDefinitionTemplate,
genericsParameter: data.genericsParameterTemplate,
Expand Down
Loading