blob: d825c8c72a29c1bafd45548e2566bf1d900606b6 [file] [log] [blame]
// Copyright (c) 2025, 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.
part of 'fragment.dart';
class PrimaryConstructorFieldDeclaration
with FieldDeclarationMixin
implements
FieldDeclaration,
FieldFragmentDeclaration,
GetterDeclaration,
Inferable,
InferredTypeListener {
final PrimaryConstructorFieldFragment _fragment;
late final FieldEncoding _encoding;
/// Whether the body of this field has been built.
///
/// Constant fields have their initializer built in the outline so we avoid
/// building them twice as part of the non-outline build.
bool hasBodyBeenBuilt = false;
PrimaryConstructorFieldDeclaration(this._fragment) {
_fragment.declaration = this;
}
@override
SourcePropertyBuilder get builder => _fragment.builder;
@override
FieldQuality get fieldQuality => FieldQuality.Concrete;
@override
DartType get fieldType => _encoding.type;
@override
// Coverage-ignore(suite): Not run.
DartType get fieldTypeInternal => _encoding.type;
@override
void set fieldTypeInternal(DartType value) {
_encoding.type = value;
}
@override
Uri get fileUri => _fragment.fileUri;
@override
GetterQuality get getterQuality => GetterQuality.Implicit;
@override
bool get hasInitializer => false;
@override
// Coverage-ignore(suite): Not run.
bool get hasSetter => false;
@override
// Coverage-ignore(suite): Not run.
shared.Expression? get initializerExpression => null;
@override
// Coverage-ignore(suite): Not run.
bool get isConst => false;
@override
// Coverage-ignore(suite): Not run.
bool get isEnumElement => false;
@override
bool get isExtensionTypeDeclaredInstanceField => false;
@override
bool get isFinal => true;
@override
bool get isLate => false;
@override
// Coverage-ignore(suite): Not run.
bool get isStatic => false;
@override
List<ClassMember> get localMembers => _encoding.localMembers;
@override
List<MetadataBuilder>? get metadata => _fragment.metadata;
@override
int get nameOffset => _fragment.nameOffset;
@override
Member get readTarget => _encoding.readTarget;
@override
TypeBuilder get type => _fragment.type;
@override
// Coverage-ignore(suite): Not run.
UriOffsetLength get uriOffset => _fragment.uriOffset;
// Coverage-ignore(suite): Not run.
/// Builds the body of this field using [initializer] as the initializer
/// expression.
void buildBody(CoreTypes coreTypes, Expression? initializer) {
assert(!hasBodyBeenBuilt, "Body has already been built for $this.");
hasBodyBeenBuilt = true;
_encoding.createBodies(coreTypes, initializer);
}
@override
Initializer buildErroneousInitializer(
Expression effect,
Expression value, {
required int fileOffset,
}) {
return _encoding.buildErroneousInitializer(
effect,
value,
fileOffset: fileOffset,
);
}
@override
// Coverage-ignore(suite): Not run.
void buildFieldInitializer(
InferenceHelper helper,
TypeInferrer typeInferrer,
CoreTypes coreTypes,
Expression? initializer,
) {
if (initializer != null) {
if (!hasBodyBeenBuilt) {
initializer = typeInferrer
.inferFieldInitializer(helper, fieldType, initializer)
.expression;
buildBody(coreTypes, initializer);
}
} else if (!hasBodyBeenBuilt) {
buildBody(coreTypes, null);
}
}
@override
void buildFieldOutlineExpressions({
required ClassHierarchy classHierarchy,
required SourceLibraryBuilder libraryBuilder,
required DeclarationBuilder? declarationBuilder,
required List<Annotatable> annotatables,
required Uri annotatablesFileUri,
required bool isClassInstanceMember,
}) {
BodyBuilderContext bodyBuilderContext = createBodyBuilderContext();
for (Annotatable annotatable in annotatables) {
buildMetadataForOutlineExpressions(
libraryBuilder: libraryBuilder,
scope: _fragment.enclosingScope,
bodyBuilderContext: bodyBuilderContext,
annotatable: annotatable,
annotatableFileUri: annotatablesFileUri,
metadata: metadata,
);
}
}
@override
void buildFieldOutlineNode(
SourceLibraryBuilder libraryBuilder,
NameScheme nameScheme,
BuildNodesCallback f,
PropertyReferences references, {
required List<TypeParameter>? classTypeParameters,
}) {
_encoding.buildOutlineNode(
libraryBuilder,
nameScheme,
references,
isAbstractOrExternal: false,
classTypeParameters: classTypeParameters,
);
if (type is! InferableTypeBuilder) {
fieldType = type.build(libraryBuilder, TypeUse.fieldType);
}
_encoding.registerMembers(f);
}
@override
void buildGetterOutlineExpressions({
required ClassHierarchy classHierarchy,
required SourceLibraryBuilder libraryBuilder,
required DeclarationBuilder? declarationBuilder,
required SourcePropertyBuilder propertyBuilder,
required Annotatable annotatable,
required Uri annotatableFileUri,
required bool isClassInstanceMember,
}) {}
@override
void buildGetterOutlineNode({
required SourceLibraryBuilder libraryBuilder,
required NameScheme nameScheme,
required BuildNodesCallback f,
required PropertyReferences? references,
required List<TypeParameter>? classTypeParameters,
}) {}
@override
// Coverage-ignore(suite): Not run.
void buildImplicitDefaultValue() {
_encoding.buildImplicitDefaultValue();
}
@override
Initializer buildImplicitInitializer() {
return _encoding.buildImplicitInitializer();
}
@override
List<Initializer> buildInitializer(
int fileOffset,
Expression value, {
required bool isSynthetic,
}) {
return _encoding.createInitializer(
fileOffset,
value,
isSynthetic: isSynthetic,
);
}
@override
void checkFieldTypes(
SourceLibraryBuilder libraryBuilder,
TypeEnvironment typeEnvironment,
SourcePropertyBuilder? setterBuilder,
) {
libraryBuilder.checkTypesInField(
typeEnvironment,
isInstanceMember: builder.isDeclarationInstanceMember,
isLate: isLate,
isExternal: false,
hasInitializer: hasInitializer,
fieldType: fieldType,
name: _fragment.name,
nameLength: _fragment.name.length,
nameOffset: nameOffset,
fileUri: fileUri,
);
}
@override
// Coverage-ignore(suite): Not run.
void checkFieldVariance(
SourceClassBuilder sourceClassBuilder,
TypeEnvironment typeEnvironment,
) {
sourceClassBuilder.checkVarianceInField(
typeEnvironment,
fieldType: fieldType,
isInstanceMember: !isStatic,
hasSetter: hasSetter,
isCovariantByDeclaration: false,
fileUri: fileUri,
fileOffset: nameOffset,
);
}
@override
void checkGetterTypes(
SourceLibraryBuilder libraryBuilder,
TypeEnvironment typeEnvironment,
SourcePropertyBuilder? setterBuilder,
) {}
@override
// Coverage-ignore(suite): Not run.
void checkGetterVariance(
SourceClassBuilder sourceClassBuilder,
TypeEnvironment typeEnvironment,
) {}
@override
int computeFieldDefaultTypes(ComputeDefaultTypeContext context) {
if (type is! OmittedTypeBuilder) {
context.reportInboundReferenceIssuesForType(type);
context.recursivelyReportGenericFunctionTypesAsBoundsForType(type);
}
return 0;
}
@override
int computeGetterDefaultTypes(ComputeDefaultTypeContext context) {
return 0;
}
@override
BodyBuilderContext createBodyBuilderContext() {
return new FieldFragmentBodyBuilderContext(
builder,
this,
isLateField: false,
isAbstractField: false,
isExternalField: false,
nameOffset: _fragment.nameOffset,
nameLength: _fragment.name.length,
isConst: false,
);
}
@override
void createFieldEncoding(SourcePropertyBuilder builder) {
_fragment.builder = builder;
SourceLibraryBuilder libraryBuilder = builder.libraryBuilder;
bool isInstanceMember = builder.isDeclarationInstanceMember;
bool isExtensionTypeMember = builder.isExtensionTypeMember;
// TODO(johnniwinther): Add support for regular fields for the primary
// constructors feature.
assert(isExtensionTypeMember && isInstanceMember);
_encoding = new RepresentationFieldEncoding(_fragment);
type.registerInferredTypeListener(this);
if (type is InferableTypeBuilder) {
// Coverage-ignore-block(suite): Not run.
// A field with no type and initializer or an instance field without
// type and initializer need to have the type inferred.
_encoding.type = new InferredType(
libraryBuilder: libraryBuilder,
typeBuilder: type,
inferType: inferType,
computeType: _computeInferredType,
fileUri: fileUri,
name: _fragment.name,
nameOffset: nameOffset,
nameLength: _fragment.name.length,
token: null,
);
type.registerInferable(this);
}
}
@override
void createGetterEncoding(
ProblemReporting problemReporting,
SourcePropertyBuilder builder,
PropertyEncodingStrategy encodingStrategy,
TypeParameterFactory typeParameterFactory,
) {}
@override
// Coverage-ignore(suite): Not run.
void ensureGetterTypes({
required SourceLibraryBuilder libraryBuilder,
required DeclarationBuilder? declarationBuilder,
required ClassMembersBuilder membersBuilder,
required Set<ClassMember>? getterOverrideDependencies,
}) {}
@override
// Coverage-ignore(suite): Not run.
void ensureTypes(
ClassMembersBuilder membersBuilder,
Set<ClassMember>? getterOverrideDependencies,
Set<ClassMember>? setterOverrideDependencies,
) {
if (getterOverrideDependencies != null ||
setterOverrideDependencies != null) {
membersBuilder.inferFieldType(
builder.declarationBuilder as SourceClassBuilder,
type,
[...?getterOverrideDependencies, ...?setterOverrideDependencies],
name: _fragment.name,
fileUri: fileUri,
nameOffset: nameOffset,
nameLength: _fragment.name.length,
isAssignable: hasSetter,
);
} else {
type.build(
builder.libraryBuilder,
TypeUse.fieldType,
hierarchy: membersBuilder.hierarchyBuilder,
);
}
}
@override
// Coverage-ignore(suite): Not run.
Iterable<Reference> getExportedGetterReferences(
PropertyReferences references,
) {
return [references.getterReference];
}
@override
// Coverage-ignore(suite): Not run.
void registerSuperCall() {
_encoding.registerSuperCall();
}
@override
// Coverage-ignore(suite): Not run.
void setCovariantByClassInternal() {
_encoding.setCovariantByClass();
}
// Coverage-ignore(suite): Not run.
DartType _computeInferredType(
ClassHierarchyBase classHierarchy,
Token? token,
) {
DartType? inferredType;
SourceLibraryBuilder libraryBuilder = builder.libraryBuilder;
DeclarationBuilder? declarationBuilder = builder.declarationBuilder;
if (token != null) {
InterfaceType? enclosingClassThisType =
declarationBuilder is SourceClassBuilder
? libraryBuilder.loader.typeInferenceEngine.coreTypes
.thisInterfaceType(
declarationBuilder.cls,
libraryBuilder.library.nonNullable,
)
: null;
LookupScope scope = _fragment.enclosingScope;
TypeInferrer typeInferrer = libraryBuilder.loader.typeInferenceEngine
.createTopLevelTypeInferrer(
fileUri,
enclosingClassThisType,
libraryBuilder,
scope,
builder.dataForTesting?.inferenceData,
);
BodyBuilderContext bodyBuilderContext = createBodyBuilderContext();
BodyBuilder bodyBuilder = libraryBuilder.loader.createBodyBuilderForField(
libraryBuilder,
bodyBuilderContext,
scope,
typeInferrer,
fileUri,
);
bodyBuilder.constantContext = ConstantContext.none;
bodyBuilder.inFieldInitializer = true;
bodyBuilder.inLateFieldInitializer = false;
Expression initializer = bodyBuilder.parseFieldInitializer(token);
inferredType = typeInferrer.inferImplicitFieldType(
bodyBuilder,
initializer,
);
} else {
inferredType = const DynamicType();
}
return inferredType;
}
}
class PrimaryConstructorFieldFragment implements Fragment {
@override
final String name;
final Uri fileUri;
final int nameOffset;
final List<MetadataBuilder>? metadata;
final TypeBuilder type;
final LookupScope enclosingScope;
final DeclarationFragment enclosingDeclaration;
final LibraryFragment enclosingCompilationUnit;
SourcePropertyBuilder? _builder;
PrimaryConstructorFieldDeclaration? _declaration;
@override
late final UriOffsetLength uriOffset = new UriOffsetLength(
fileUri,
nameOffset,
name.length,
);
PrimaryConstructorFieldFragment({
required this.name,
required this.fileUri,
required this.nameOffset,
required this.metadata,
required this.type,
required this.enclosingScope,
required this.enclosingDeclaration,
required this.enclosingCompilationUnit,
});
@override
SourcePropertyBuilder get builder {
assert(_builder != null, "Builder has not been computed for $this.");
return _builder!;
}
void set builder(SourcePropertyBuilder value) {
assert(_builder == null, "Builder has already been computed for $this.");
_builder = value;
}
// Coverage-ignore(suite): Not run.
PrimaryConstructorFieldDeclaration get declaration {
assert(
_declaration != null,
"Declaration has not been computed for $this.",
);
return _declaration!;
}
void set declaration(PrimaryConstructorFieldDeclaration value) {
assert(
_declaration == null,
"Declaration has already been computed for $this.",
);
_declaration = value;
}
}