blob: b73a8ba929db6e7506836a7ac836eedd9b832e6b [file] [log] [blame]
// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
// 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/nullability_suffix.dart';
import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer/dart/element/type_visitor.dart';
import 'package:analyzer/src/dart/ast/ast.dart';
import 'package:analyzer/src/dart/ast/extensions.dart';
import 'package:analyzer/src/dart/element/type.dart';
import 'package:analyzer/src/dart/element/type_system.dart';
import 'package:analyzer/src/dart/element/type_visitor.dart';
import 'package:analyzer/src/summary2/type_builder.dart';
/// The type builder for a [RecordType].
class RecordTypeBuilder extends TypeBuilder {
/// The type system of the library with the type name.
final TypeSystemImpl typeSystem;
/// The node for which this builder is created.
final RecordTypeAnnotationImpl node;
/// The ordered list of field types, first positional, then named.
final List<DartType> fieldTypes;
@override
final NullabilitySuffix nullabilitySuffix;
/// This flag is set to `true` while building this type.
bool _isBuilding = false;
/// The actual built type, not a [TypeBuilder] anymore.
RecordTypeImpl? _type;
RecordTypeBuilder({
required this.typeSystem,
required this.node,
required this.fieldTypes,
required this.nullabilitySuffix,
});
factory RecordTypeBuilder.of(
TypeSystemImpl typeSystem,
RecordTypeAnnotationImpl node,
) {
return RecordTypeBuilder(
typeSystem: typeSystem,
node: node,
fieldTypes: node.fields.map((field) => field.type.typeOrThrow).toList(),
nullabilitySuffix:
node.question != null
? NullabilitySuffix.question
: NullabilitySuffix.none,
);
}
@override
R accept<R>(TypeVisitor<R> visitor) {
if (visitor is LinkingTypeVisitor<R>) {
var visitor2 = visitor as LinkingTypeVisitor<R>;
return visitor2.visitRecordTypeBuilder(this);
} else {
throw StateError('Should not happen outside linking.');
}
}
@override
RecordTypeImpl build() {
var type = _type;
if (type != null) {
return type;
}
if (_isBuilding) {
return _type = _buildRecordType(recursionFound: true);
}
_isBuilding = true;
try {
return _type = _buildRecordType();
} finally {
_isBuilding = false;
}
}
@override
String toString() {
return node.toSource();
}
RecordTypeImpl _buildRecordType({bool recursionFound = false}) {
var fieldTypeIndex = 0;
DartType nextFieldType() {
if (recursionFound) {
return typeSystem.typeProvider.dynamicType;
} else {
var type = fieldTypes[fieldTypeIndex++];
return _buildType(type);
}
}
var positionalFields =
node.positionalFields.map((field) {
return RecordTypePositionalFieldImpl(type: nextFieldType());
}).toList();
var namedFields =
node.namedFields?.fields.map((field) {
return RecordTypeNamedFieldImpl(
name: field.name.lexeme,
type: nextFieldType(),
);
}).toList();
return node.type = RecordTypeImpl(
positionalFields: positionalFields,
namedFields: namedFields ?? const [],
nullabilitySuffix: nullabilitySuffix,
);
}
/// If the [type] is a [TypeBuilder], build it; otherwise return as is.
static DartType _buildType(DartType type) {
if (type is TypeBuilder) {
return type.build();
} else {
return type;
}
}
}