| // Copyright (c) 2023, 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:kernel/ast.dart'; |
| import 'package:kernel/class_hierarchy.dart'; |
| import 'package:kernel/core_types.dart'; |
| import 'package:kernel/reference_from_index.dart'; |
| import 'package:kernel/type_algebra.dart'; |
| import 'package:kernel/type_environment.dart'; |
| |
| import '../base/messages.dart'; |
| import '../base/modifiers.dart'; |
| import '../base/name_space.dart'; |
| import '../base/problems.dart'; |
| import '../base/scope.dart'; |
| import '../builder/augmentation_iterator.dart'; |
| import '../builder/builder.dart'; |
| import '../builder/constructor_reference_builder.dart'; |
| import '../builder/declaration_builders.dart'; |
| import '../builder/formal_parameter_builder.dart'; |
| import '../builder/library_builder.dart'; |
| import '../builder/member_builder.dart'; |
| import '../builder/metadata_builder.dart'; |
| import '../builder/name_iterator.dart'; |
| import '../builder/record_type_builder.dart'; |
| import '../builder/type_builder.dart'; |
| import '../fragment/fragment.dart'; |
| import '../kernel/body_builder_context.dart'; |
| import '../kernel/hierarchy/hierarchy_builder.dart'; |
| import '../kernel/kernel_helper.dart'; |
| import '../type_inference/type_inference_engine.dart'; |
| import 'class_declaration.dart'; |
| import 'name_scheme.dart'; |
| import 'source_builder_mixins.dart'; |
| import 'source_constructor_builder.dart'; |
| import 'source_factory_builder.dart'; |
| import 'source_field_builder.dart'; |
| import 'source_library_builder.dart'; |
| import 'source_member_builder.dart'; |
| import 'type_parameter_scope_builder.dart'; |
| |
| class SourceExtensionTypeDeclarationBuilder |
| extends ExtensionTypeDeclarationBuilderImpl |
| with |
| SourceDeclarationBuilderMixin, |
| ClassDeclarationMixin, |
| SourceTypedDeclarationBuilderMixin |
| implements |
| Comparable<SourceExtensionTypeDeclarationBuilder>, |
| ClassDeclaration { |
| @override |
| final SourceLibraryBuilder parent; |
| |
| @override |
| final int charOffset; |
| |
| @override |
| final String name; |
| |
| @override |
| final Uri fileUri; |
| |
| @override |
| final List<MetadataBuilder>? metadata; |
| |
| final Modifiers _modifiers; |
| |
| @override |
| final List<ConstructorReferenceBuilder>? constructorReferences; |
| |
| final ExtensionTypeDeclaration _extensionTypeDeclaration; |
| |
| SourceExtensionTypeDeclarationBuilder? _origin; |
| |
| MergedClassMemberScope? _mergedScope; |
| |
| final DeclarationNameSpaceBuilder _nameSpaceBuilder; |
| |
| late final LookupScope _scope; |
| |
| late final DeclarationNameSpace _nameSpace; |
| |
| late final ConstructorScope _constructorScope; |
| |
| @override |
| final List<NominalVariableBuilder>? typeParameters; |
| |
| @override |
| final LookupScope typeParameterScope; |
| |
| @override |
| List<TypeBuilder>? interfaceBuilders; |
| |
| FieldFragment? _representationFieldFragment; |
| |
| SourceFieldBuilder? _representationFieldBuilder; |
| |
| final IndexedContainer? indexedContainer; |
| |
| Nullability? _nullability; |
| |
| SourceExtensionTypeDeclarationBuilder( |
| {required this.metadata, |
| required Modifiers modifiers, |
| required this.name, |
| required this.typeParameters, |
| required this.interfaceBuilders, |
| required this.typeParameterScope, |
| required DeclarationNameSpaceBuilder nameSpaceBuilder, |
| required SourceLibraryBuilder enclosingLibraryBuilder, |
| required this.constructorReferences, |
| required this.fileUri, |
| required int startOffset, |
| required int nameOffset, |
| required int endOffset, |
| required this.indexedContainer, |
| required FieldFragment? representationFieldFragment}) |
| : parent = enclosingLibraryBuilder, |
| charOffset = nameOffset, |
| _modifiers = modifiers, |
| _extensionTypeDeclaration = new ExtensionTypeDeclaration( |
| name: name, |
| fileUri: fileUri, |
| typeParameters: NominalVariableBuilder.typeParametersFromBuilders( |
| typeParameters), |
| reference: indexedContainer?.reference) |
| ..fileOffset = nameOffset, |
| _nameSpaceBuilder = nameSpaceBuilder, |
| _representationFieldFragment = representationFieldFragment; |
| |
| @override |
| LookupScope get scope => _scope; |
| |
| @override |
| DeclarationNameSpace get nameSpace => _nameSpace; |
| |
| @override |
| ConstructorScope get constructorScope => _constructorScope; |
| |
| @override |
| // Coverage-ignore(suite): Not run. |
| bool get isConst => _modifiers.isConst; |
| |
| @override |
| // Coverage-ignore(suite): Not run. |
| bool get isFinal => _modifiers.isFinal; |
| |
| @override |
| // Coverage-ignore(suite): Not run. |
| bool get isStatic => _modifiers.isStatic; |
| |
| @override |
| bool get isAugment => _modifiers.isAugment; |
| |
| SourceFieldBuilder? get representationFieldBuilder { |
| if (_representationFieldBuilder == null) { |
| _representationFieldBuilder = _representationFieldFragment?.builder; |
| _representationFieldFragment = null; |
| } |
| return _representationFieldBuilder; |
| } |
| |
| @override |
| void buildScopes(LibraryBuilder coreLibrary) { |
| _nameSpace = _nameSpaceBuilder.buildNameSpace( |
| loader: libraryBuilder.loader, |
| problemReporting: libraryBuilder, |
| enclosingLibraryBuilder: libraryBuilder, |
| declarationBuilder: this, |
| indexedLibrary: libraryBuilder.indexedLibrary, |
| indexedContainer: indexedContainer, |
| containerType: ContainerType.ExtensionType, |
| containerName: new ClassName(name)); |
| _scope = new NameSpaceLookupScope( |
| _nameSpace, ScopeKind.declaration, "extension type $name", |
| parent: typeParameterScope); |
| _constructorScope = |
| new DeclarationNameSpaceConstructorScope(name, _nameSpace); |
| } |
| |
| @override |
| SourceLibraryBuilder get libraryBuilder => |
| super.libraryBuilder as SourceLibraryBuilder; |
| |
| @override |
| TypeBuilder? get declaredRepresentationTypeBuilder => |
| representationFieldBuilder?.type; |
| |
| @override |
| SourceExtensionTypeDeclarationBuilder get origin => _origin ?? this; |
| |
| // Coverage-ignore(suite): Not run. |
| // TODO(johnniwinther): Add merged scope for extension type declarations. |
| MergedClassMemberScope get mergedScope => _mergedScope ??= isAugmenting |
| ? origin.mergedScope |
| : throw new UnimplementedError( |
| "SourceExtensionTypeDeclarationBuilder.mergedScope"); |
| |
| @override |
| ExtensionTypeDeclaration get extensionTypeDeclaration => isAugmenting |
| ? |
| // Coverage-ignore(suite): Not run. |
| origin._extensionTypeDeclaration |
| : _extensionTypeDeclaration; |
| |
| @override |
| Annotatable get annotatable => extensionTypeDeclaration; |
| |
| @override |
| int compareTo(SourceExtensionTypeDeclarationBuilder other) { |
| int result = "$fileUri".compareTo("${other.fileUri}"); |
| if (result != 0) return result; |
| return charOffset.compareTo(other.charOffset); |
| } |
| |
| /// Builds the [ExtensionTypeDeclaration] for this extension type declaration |
| /// builder and inserts the members into the [Library] of [libraryBuilder]. |
| /// |
| /// [addMembersToLibrary] is `true` if the extension type members should be |
| /// added to the library. This is `false` if the extension type declaration is |
| /// in conflict with another library member. In this case, the extension type |
| /// member should not be added to the library to avoid name clashes with other |
| /// members in the library. |
| ExtensionTypeDeclaration build(LibraryBuilder coreLibrary, |
| {required bool addMembersToLibrary}) { |
| if (interfaceBuilders != null) { |
| for (int i = 0; i < interfaceBuilders!.length; ++i) { |
| TypeBuilder typeBuilder = interfaceBuilders![i]; |
| TypeAliasBuilder? aliasBuilder = |
| typeBuilder.declaration is TypeAliasBuilder |
| ? typeBuilder.declaration as TypeAliasBuilder |
| : null; |
| DartType interface = typeBuilder.build( |
| libraryBuilder, TypeUse.extensionTypeImplementsType); |
| Message? errorMessage; |
| List<LocatedMessage>? errorContext; |
| |
| if (typeParameters?.isNotEmpty ?? false) { |
| for (NominalVariableBuilder variable in typeParameters!) { |
| Variance variance = typeBuilder |
| .computeTypeVariableBuilderVariance(variable, |
| sourceLoader: libraryBuilder.loader) |
| .variance!; |
| if (!variance.greaterThanOrEqual(variable.variance)) { |
| if (variable.parameter.isLegacyCovariant) { |
| errorMessage = |
| templateWrongTypeParameterVarianceInSuperinterface |
| .withArguments(variable.name, interface); |
| } else { |
| // Coverage-ignore-block(suite): Not run. |
| errorMessage = |
| templateInvalidTypeVariableInSupertypeWithVariance |
| .withArguments(variable.variance.keyword, variable.name, |
| variance.keyword, typeBuilder.typeName!.name); |
| } |
| } |
| } |
| if (errorMessage != null) { |
| libraryBuilder.addProblem(errorMessage, typeBuilder.charOffset!, |
| noLength, typeBuilder.fileUri, |
| context: errorContext); |
| errorMessage = null; |
| } |
| } |
| |
| if (interface is ExtensionType) { |
| if (interface.nullability == Nullability.nullable) { |
| errorMessage = templateSuperExtensionTypeIsNullableAliased |
| .withArguments(typeBuilder.fullNameForErrors, interface); |
| if (aliasBuilder != null) { |
| errorContext = [ |
| messageTypedefCause.withLocation( |
| aliasBuilder.fileUri, aliasBuilder.charOffset, noLength), |
| ]; |
| } |
| } else { |
| extensionTypeDeclaration.implements.add(interface); |
| } |
| } else if (interface is InterfaceType) { |
| if (interface.isPotentiallyNullable) { |
| if (typeBuilder.nullabilityBuilder.isNullable) { |
| errorMessage = templateNullableInterfaceError |
| .withArguments(typeBuilder.fullNameForErrors); |
| } else { |
| errorMessage = templateSuperExtensionTypeIsNullableAliased |
| .withArguments(typeBuilder.fullNameForErrors, interface); |
| if (aliasBuilder != null) { |
| errorContext = [ |
| messageTypedefCause.withLocation( |
| aliasBuilder.fileUri, aliasBuilder.charOffset, noLength), |
| ]; |
| } |
| } |
| } else { |
| Class cls = interface.classNode; |
| if (LibraryBuilder.isFunction(cls, coreLibrary) || |
| LibraryBuilder.isRecord(cls, coreLibrary)) { |
| if (aliasBuilder != null) { |
| // Coverage-ignore-block(suite): Not run. |
| errorMessage = templateSuperExtensionTypeIsIllegalAliased |
| .withArguments(typeBuilder.fullNameForErrors, interface); |
| errorContext = [ |
| messageTypedefCause.withLocation( |
| aliasBuilder.fileUri, aliasBuilder.charOffset, noLength), |
| ]; |
| } else { |
| errorMessage = templateSuperExtensionTypeIsIllegal |
| .withArguments(typeBuilder.fullNameForErrors); |
| } |
| } else { |
| extensionTypeDeclaration.implements.add(interface); |
| } |
| } |
| } else if (interface is TypeParameterType) { |
| errorMessage = templateSuperExtensionTypeIsTypeVariable |
| .withArguments(typeBuilder.fullNameForErrors); |
| if (aliasBuilder != null) { |
| // Coverage-ignore-block(suite): Not run. |
| errorContext = [ |
| messageTypedefCause.withLocation( |
| aliasBuilder.fileUri, aliasBuilder.charOffset, noLength), |
| ]; |
| } |
| } else { |
| if (aliasBuilder != null) { |
| errorMessage = templateSuperExtensionTypeIsIllegalAliased |
| .withArguments(typeBuilder.fullNameForErrors, interface); |
| errorContext = [ |
| messageTypedefCause.withLocation( |
| aliasBuilder.fileUri, aliasBuilder.charOffset, noLength), |
| ]; |
| } else { |
| errorMessage = templateSuperExtensionTypeIsIllegal |
| .withArguments(typeBuilder.fullNameForErrors); |
| } |
| } |
| if (errorMessage != null) { |
| libraryBuilder.addProblem(errorMessage, typeBuilder.charOffset!, |
| noLength, typeBuilder.fileUri, |
| context: errorContext); |
| } |
| } |
| } |
| |
| DartType representationType; |
| String representationName; |
| if (representationFieldBuilder != null) { |
| TypeBuilder typeBuilder = representationFieldBuilder!.type; |
| if (typeBuilder.isExplicit) { |
| if (_checkRepresentationDependency(typeBuilder, this, {this}, {})) { |
| representationType = const InvalidType(); |
| } else { |
| representationType = |
| typeBuilder.build(libraryBuilder, TypeUse.fieldType); |
| if (typeParameters != null) { |
| IncludesTypeParametersNonCovariantly checker = |
| new IncludesTypeParametersNonCovariantly( |
| extensionTypeDeclaration.typeParameters, |
| // We are checking the returned type (field/getter type or return |
| // type of a method) and this is a covariant position. |
| initialVariance: Variance.covariant); |
| if (representationType.accept(checker)) { |
| libraryBuilder.addProblem( |
| messageNonCovariantTypeParameterInRepresentationType, |
| typeBuilder.charOffset!, |
| noLength, |
| typeBuilder.fileUri); |
| } |
| } |
| if (isBottom(representationType)) { |
| libraryBuilder.addProblem( |
| messageExtensionTypeRepresentationTypeBottom, |
| representationFieldBuilder!.charOffset, |
| representationFieldBuilder!.name.length, |
| representationFieldBuilder!.fileUri); |
| representationType = const InvalidType(); |
| } |
| } |
| } else { |
| representationType = const DynamicType(); |
| } |
| representationName = representationFieldBuilder!.name; |
| } else { |
| representationType = const InvalidType(); |
| representationName = '#'; |
| } |
| _extensionTypeDeclaration.declaredRepresentationType = representationType; |
| _extensionTypeDeclaration.representationName = representationName; |
| buildInternal(coreLibrary, addMembersToLibrary: addMembersToLibrary); |
| checkConstructorStaticConflict(); |
| |
| return _extensionTypeDeclaration; |
| } |
| |
| bool _checkRepresentationDependency( |
| TypeBuilder? typeBuilder, |
| ExtensionTypeDeclarationBuilder rootExtensionTypeDeclaration, |
| Set<ExtensionTypeDeclarationBuilder> seenExtensionTypeDeclarations, |
| Set<TypeAliasBuilder> usedTypeAliasBuilders) { |
| TypeBuilder? unaliased; |
| if (typeBuilder != null) { |
| typeBuilder.build( |
| libraryBuilder, TypeUse.extensionTypeRepresentationType); |
| unaliased = typeBuilder.unalias( |
| usedTypeAliasBuilders: usedTypeAliasBuilders, |
| // We allow creating new type variables during unaliasing. This type |
| // variables are short-lived and therefore don't need to be bound. |
| unboundTypeVariables: []); |
| } |
| switch (unaliased) { |
| case NamedTypeBuilder( |
| :TypeDeclarationBuilder? declaration, |
| typeArguments: List<TypeBuilder>? arguments |
| ): |
| if (declaration is ExtensionTypeDeclarationBuilder) { |
| bool declarationSeenFirstTime = |
| seenExtensionTypeDeclarations.add(declaration); |
| if (declaration == rootExtensionTypeDeclaration) { |
| List<LocatedMessage> context = []; |
| for (ExtensionTypeDeclarationBuilder extensionTypeDeclarationBuilder |
| in seenExtensionTypeDeclarations) { |
| if (extensionTypeDeclarationBuilder != this) { |
| context.add(messageExtensionTypeDeclarationCause.withLocation( |
| extensionTypeDeclarationBuilder.fileUri, |
| extensionTypeDeclarationBuilder.charOffset, |
| extensionTypeDeclarationBuilder.name.length)); |
| } |
| } |
| for (TypeAliasBuilder typeAliasBuilder in usedTypeAliasBuilders) { |
| context.add(messageTypedefCause.withLocation( |
| typeAliasBuilder.fileUri, |
| typeAliasBuilder.charOffset, |
| typeAliasBuilder.name.length)); |
| } |
| libraryBuilder.addProblem( |
| messageCyclicRepresentationDependency, |
| representationFieldBuilder!.type.charOffset!, |
| noLength, |
| representationFieldBuilder!.type.fileUri, |
| context: context); |
| return true; |
| } else { |
| TypeBuilder? representationTypeBuilder = |
| declaration.declaredRepresentationTypeBuilder; |
| if (declarationSeenFirstTime && representationTypeBuilder != null) { |
| if (_checkRepresentationDependency( |
| representationTypeBuilder, |
| rootExtensionTypeDeclaration, |
| seenExtensionTypeDeclarations.toSet(), |
| usedTypeAliasBuilders.toSet())) { |
| return true; |
| } |
| } |
| } |
| } |
| if (arguments != null) { |
| for (TypeBuilder typeArgument in arguments) { |
| if (_checkRepresentationDependency( |
| typeArgument, |
| rootExtensionTypeDeclaration, |
| seenExtensionTypeDeclarations.toSet(), |
| usedTypeAliasBuilders.toSet())) { |
| return true; |
| } |
| } |
| } else if (declaration != null && declaration.typeVariablesCount > 0) { |
| List<TypeVariableBuilder>? typeParameters; |
| switch (declaration) { |
| case ClassBuilder(): |
| typeParameters = declaration.typeVariables; |
| case TypeAliasBuilder(): |
| // Coverage-ignore(suite): Not run. |
| typeParameters = declaration.typeVariables; |
| case ExtensionTypeDeclarationBuilder(): |
| typeParameters = declaration.typeParameters; |
| // Coverage-ignore(suite): Not run. |
| case BuiltinTypeDeclarationBuilder(): |
| case InvalidTypeDeclarationBuilder(): |
| case OmittedTypeDeclarationBuilder(): |
| case ExtensionBuilder(): |
| case TypeVariableBuilder(): |
| } |
| if (typeParameters != null) { |
| for (int i = 0; i < typeParameters.length; i++) { |
| TypeVariableBuilder typeParameter = typeParameters[i]; |
| if (_checkRepresentationDependency( |
| typeParameter.defaultType!, |
| rootExtensionTypeDeclaration, |
| seenExtensionTypeDeclarations.toSet(), |
| usedTypeAliasBuilders.toSet())) { |
| return true; |
| } |
| } |
| } |
| } |
| case FunctionTypeBuilder( |
| :List<StructuralVariableBuilder>? typeVariables, |
| :List<ParameterBuilder>? formals, |
| :TypeBuilder returnType |
| ): |
| if (_checkRepresentationDependency( |
| returnType, |
| rootExtensionTypeDeclaration, |
| seenExtensionTypeDeclarations.toSet(), |
| usedTypeAliasBuilders.toSet())) { |
| return true; |
| } |
| if (formals != null) { |
| for (ParameterBuilder formal in formals) { |
| if (_checkRepresentationDependency( |
| formal.type, |
| rootExtensionTypeDeclaration, |
| seenExtensionTypeDeclarations.toSet(), |
| usedTypeAliasBuilders.toSet())) { |
| return true; |
| } |
| } |
| } |
| if (typeVariables != null) { |
| for (StructuralVariableBuilder typeVariable in typeVariables) { |
| TypeBuilder? bound = typeVariable.bound; |
| if (_checkRepresentationDependency( |
| bound, |
| rootExtensionTypeDeclaration, |
| seenExtensionTypeDeclarations.toSet(), |
| usedTypeAliasBuilders.toSet())) { |
| return true; |
| } |
| } |
| } |
| case RecordTypeBuilder( |
| :List<RecordTypeFieldBuilder>? positionalFields, |
| :List<RecordTypeFieldBuilder>? namedFields |
| ): |
| if (positionalFields != null) { |
| for (RecordTypeFieldBuilder field in positionalFields) { |
| if (_checkRepresentationDependency( |
| field.type, |
| rootExtensionTypeDeclaration, |
| seenExtensionTypeDeclarations.toSet(), |
| usedTypeAliasBuilders.toSet())) { |
| return true; |
| } |
| } |
| } |
| if (namedFields != null) { |
| for (RecordTypeFieldBuilder field in namedFields) { |
| if (_checkRepresentationDependency( |
| field.type, |
| rootExtensionTypeDeclaration, |
| seenExtensionTypeDeclarations.toSet(), |
| usedTypeAliasBuilders.toSet())) { |
| return true; |
| } |
| } |
| } |
| case OmittedTypeBuilder(): |
| case FixedTypeBuilder(): |
| case InvalidTypeBuilder(): |
| case null: |
| } |
| return false; |
| } |
| |
| void checkSupertypes( |
| CoreTypes coreTypes, ClassHierarchyBuilder hierarchyBuilder) { |
| if (interfaceBuilders != null) { |
| Map<TypeDeclarationBuilder, ({int count, int offset})>? |
| duplicationProblems; |
| Set<TypeDeclarationBuilder> implemented = {}; |
| for (int i = 0; i < interfaceBuilders!.length; ++i) { |
| TypeBuilder typeBuilder = interfaceBuilders![i]; |
| DartType interface = typeBuilder.build( |
| libraryBuilder, TypeUse.extensionTypeImplementsType); |
| if (interface is InterfaceType) { |
| if (!hierarchyBuilder.types.isSubtypeOf(declaredRepresentationType, |
| interface, SubtypeCheckMode.withNullabilities)) { |
| libraryBuilder.addProblem( |
| templateInvalidExtensionTypeSuperInterface.withArguments( |
| interface, declaredRepresentationType, name), |
| typeBuilder.charOffset!, |
| noLength, |
| typeBuilder.fileUri); |
| } |
| } else if (interface is ExtensionType) { |
| if (!hierarchyBuilder.types.isSubtypeOf(declaredRepresentationType, |
| interface, SubtypeCheckMode.withNullabilities)) { |
| DartType instantiatedImplementedRepresentationType = |
| Substitution.fromExtensionType(interface).substituteType( |
| interface |
| .extensionTypeDeclaration.declaredRepresentationType); |
| if (!hierarchyBuilder.types.isSubtypeOf( |
| declaredRepresentationType, |
| instantiatedImplementedRepresentationType, |
| SubtypeCheckMode.withNullabilities)) { |
| libraryBuilder.addProblem( |
| templateInvalidExtensionTypeSuperExtensionType.withArguments( |
| declaredRepresentationType, |
| name, |
| instantiatedImplementedRepresentationType, |
| interface), |
| typeBuilder.charOffset!, |
| noLength, |
| typeBuilder.fileUri); |
| } |
| } |
| } |
| |
| TypeDeclarationBuilder? typeDeclaration = |
| typeBuilder.computeUnaliasedDeclaration(isUsedAsClass: false); |
| if (typeDeclaration is ClassBuilder || |
| typeDeclaration is ExtensionTypeDeclarationBuilder) { |
| if (!implemented.add(typeDeclaration!)) { |
| duplicationProblems ??= {}; |
| switch (duplicationProblems[typeDeclaration]) { |
| case (:var count, :var offset): |
| duplicationProblems[typeDeclaration] = |
| (count: count + 1, offset: offset); |
| case null: |
| duplicationProblems[typeDeclaration] = ( |
| count: 1, |
| offset: typeBuilder.charOffset ?? TreeNode.noOffset |
| ); |
| } |
| } |
| } |
| } |
| |
| if (duplicationProblems != null) { |
| for (var MapEntry(key: typeDeclaration, value: (:count, :offset)) |
| in duplicationProblems.entries) { |
| addProblem( |
| templateImplementsRepeated.withArguments( |
| typeDeclaration.name, count), |
| offset, |
| noLength); |
| } |
| } |
| } |
| } |
| |
| @override |
| Nullability computeNullability( |
| {Map<ExtensionTypeDeclarationBuilder, TraversalState>? |
| traversalState}) => |
| _nullability ??= _computeNullability(traversalState: traversalState); |
| |
| Nullability _computeNullabilityFromType(TypeBuilder typeBuilder, |
| {required Map<ExtensionTypeDeclarationBuilder, TraversalState> |
| traversalState}) { |
| Nullability nullability = typeBuilder.nullabilityBuilder.build(); |
| TypeDeclarationBuilder? declaration = typeBuilder.declaration; |
| switch (declaration) { |
| case TypeAliasBuilder(): |
| return combineNullabilitiesForSubstitution( |
| inner: _computeNullabilityFromType( |
| declaration.unalias(typeBuilder.typeArguments, |
| unboundTypeVariables: [])!, |
| traversalState: traversalState), |
| outer: nullability); |
| case ExtensionTypeDeclarationBuilder(): |
| return combineNullabilitiesForSubstitution( |
| inner: |
| declaration.computeNullability(traversalState: traversalState), |
| outer: nullability); |
| case ClassBuilder(): |
| // Coverage-ignore(suite): Not run. |
| case NominalVariableBuilder(): |
| // Coverage-ignore(suite): Not run. |
| case StructuralVariableBuilder(): |
| // Coverage-ignore(suite): Not run. |
| case ExtensionBuilder(): |
| // Coverage-ignore(suite): Not run. |
| case BuiltinTypeDeclarationBuilder(): |
| // Coverage-ignore(suite): Not run. |
| case InvalidTypeDeclarationBuilder(): |
| // Coverage-ignore(suite): Not run. |
| case OmittedTypeDeclarationBuilder(): |
| case null: |
| return nullability; |
| } |
| } |
| |
| Nullability _computeNullability( |
| {Map<ExtensionTypeDeclarationBuilder, TraversalState>? traversalState}) { |
| traversalState ??= {}; |
| Nullability nullability = Nullability.undetermined; |
| switch (traversalState[this] ??= TraversalState.unvisited) { |
| case TraversalState.unvisited: |
| traversalState[this] = TraversalState.active; |
| List<TypeBuilder>? interfaceBuilders = this.interfaceBuilders; |
| if (interfaceBuilders != null) { |
| for (TypeBuilder interfaceBuilder in interfaceBuilders) { |
| Nullability interfaceNullability = _computeNullabilityFromType( |
| interfaceBuilder, |
| traversalState: traversalState); |
| if (interfaceNullability == Nullability.nonNullable) { |
| nullability = Nullability.nonNullable; |
| break; |
| } |
| } |
| } |
| traversalState[this] = TraversalState.visited; |
| // Coverage-ignore(suite): Not run. |
| case TraversalState.active: |
| case TraversalState.visited: |
| traversalState[this] = TraversalState.visited; |
| } |
| return nullability; |
| } |
| |
| void checkRedirectingFactories(TypeEnvironment typeEnvironment) { |
| Iterator<SourceFactoryBuilder> iterator = |
| nameSpace.filteredConstructorIterator<SourceFactoryBuilder>( |
| parent: this, includeDuplicates: true, includeAugmentations: true); |
| while (iterator.moveNext()) { |
| iterator.current.checkRedirectingFactories(typeEnvironment); |
| } |
| } |
| |
| @override |
| void buildOutlineExpressions(ClassHierarchy classHierarchy, |
| List<DelayedDefaultValueCloner> delayedDefaultValueCloners) { |
| super.buildOutlineExpressions(classHierarchy, delayedDefaultValueCloners); |
| |
| Iterator<SourceMemberBuilder> iterator = |
| nameSpace.filteredConstructorIterator( |
| parent: this, includeDuplicates: false, includeAugmentations: true); |
| while (iterator.moveNext()) { |
| iterator.current |
| .buildOutlineExpressions(classHierarchy, delayedDefaultValueCloners); |
| } |
| } |
| |
| @override |
| void addMemberInternal(SourceMemberBuilder memberBuilder, |
| BuiltMemberKind memberKind, Member member, Member? tearOff) { |
| switch (memberKind) { |
| case BuiltMemberKind.Constructor: |
| case BuiltMemberKind.RedirectingFactory: |
| case BuiltMemberKind.Field: |
| case BuiltMemberKind.Method: |
| case BuiltMemberKind.Factory: |
| case BuiltMemberKind.ExtensionMethod: |
| case BuiltMemberKind.ExtensionGetter: |
| case BuiltMemberKind.ExtensionSetter: |
| case BuiltMemberKind.ExtensionOperator: |
| case BuiltMemberKind.ExtensionField: |
| case BuiltMemberKind.LateIsSetField: |
| case BuiltMemberKind.ExtensionTypeConstructor: |
| case BuiltMemberKind.ExtensionTypeFactory: |
| case BuiltMemberKind.ExtensionTypeRedirectingFactory: |
| case BuiltMemberKind.ExtensionTypeMethod: |
| case BuiltMemberKind.ExtensionTypeGetter: |
| case BuiltMemberKind.LateGetter: |
| case BuiltMemberKind.ExtensionTypeSetter: |
| case BuiltMemberKind.LateSetter: |
| case BuiltMemberKind.ExtensionTypeOperator: |
| // Coverage-ignore(suite): Not run. |
| unhandled( |
| "${memberBuilder.runtimeType}:${memberKind}", |
| "addMemberInternal", |
| memberBuilder.charOffset, |
| memberBuilder.fileUri); |
| case BuiltMemberKind.ExtensionTypeRepresentationField: |
| assert(tearOff == null, "Unexpected tear-off $tearOff"); |
| extensionTypeDeclaration.addProcedure(member as Procedure); |
| } |
| } |
| |
| @override |
| void addMemberDescriptorInternal( |
| SourceMemberBuilder memberBuilder, |
| BuiltMemberKind memberKind, |
| Reference memberReference, |
| Reference? tearOffReference) { |
| String name = memberBuilder.name; |
| ExtensionTypeMemberKind kind; |
| switch (memberKind) { |
| case BuiltMemberKind.Constructor: |
| case BuiltMemberKind.RedirectingFactory: |
| case BuiltMemberKind.Field: |
| case BuiltMemberKind.Method: |
| case BuiltMemberKind.Factory: |
| case BuiltMemberKind.ExtensionMethod: |
| case BuiltMemberKind.ExtensionGetter: |
| case BuiltMemberKind.ExtensionSetter: |
| case BuiltMemberKind.ExtensionOperator: |
| case BuiltMemberKind.ExtensionTypeRepresentationField: |
| // Coverage-ignore(suite): Not run. |
| unhandled("${memberBuilder.runtimeType}:${memberKind}", "buildMembers", |
| memberBuilder.charOffset, memberBuilder.fileUri); |
| case BuiltMemberKind.ExtensionField: |
| case BuiltMemberKind.LateIsSetField: |
| kind = ExtensionTypeMemberKind.Field; |
| break; |
| case BuiltMemberKind.ExtensionTypeConstructor: |
| kind = ExtensionTypeMemberKind.Constructor; |
| break; |
| case BuiltMemberKind.ExtensionTypeFactory: |
| kind = ExtensionTypeMemberKind.Factory; |
| break; |
| case BuiltMemberKind.ExtensionTypeRedirectingFactory: |
| kind = ExtensionTypeMemberKind.RedirectingFactory; |
| break; |
| case BuiltMemberKind.ExtensionTypeMethod: |
| kind = ExtensionTypeMemberKind.Method; |
| break; |
| case BuiltMemberKind.ExtensionTypeGetter: |
| case BuiltMemberKind.LateGetter: |
| kind = ExtensionTypeMemberKind.Getter; |
| break; |
| case BuiltMemberKind.ExtensionTypeSetter: |
| case BuiltMemberKind.LateSetter: |
| kind = ExtensionTypeMemberKind.Setter; |
| break; |
| case BuiltMemberKind.ExtensionTypeOperator: |
| kind = ExtensionTypeMemberKind.Operator; |
| break; |
| } |
| extensionTypeDeclaration.memberDescriptors.add( |
| new ExtensionTypeMemberDescriptor( |
| name: new Name(name, libraryBuilder.library), |
| memberReference: memberReference, |
| tearOffReference: tearOffReference, |
| isStatic: memberBuilder.isStatic, |
| kind: kind)); |
| } |
| |
| @override |
| // Coverage-ignore(suite): Not run. |
| void applyAugmentation(Builder augmentation) { |
| if (augmentation is SourceExtensionTypeDeclarationBuilder) { |
| augmentation._origin = this; |
| nameSpace.forEachLocalMember((String name, Builder member) { |
| Builder? memberAugmentation = |
| augmentation.nameSpace.lookupLocalMember(name, setter: false); |
| if (memberAugmentation != null) { |
| member.applyAugmentation(memberAugmentation); |
| } |
| }); |
| nameSpace.forEachLocalSetter((String name, Builder member) { |
| Builder? memberAugmentation = |
| augmentation.nameSpace.lookupLocalMember(name, setter: true); |
| if (memberAugmentation != null) { |
| member.applyAugmentation(memberAugmentation); |
| } |
| }); |
| |
| // TODO(johnniwinther): Check that type parameters and on-type match |
| // with origin declaration. |
| } else { |
| libraryBuilder.addProblem(messagePatchDeclarationMismatch, |
| augmentation.charOffset, noLength, augmentation.fileUri, context: [ |
| messagePatchDeclarationOrigin.withLocation( |
| fileUri, charOffset, noLength) |
| ]); |
| } |
| } |
| |
| /// Looks up the constructor by [name] on the class built by this class |
| /// builder. |
| SourceExtensionTypeConstructorBuilder? lookupConstructor(Name name) { |
| if (name.text == "new") { |
| // Coverage-ignore-block(suite): Not run. |
| name = new Name("", name.library); |
| } |
| |
| Builder? builder = nameSpace.lookupConstructor(name.text); |
| if (builder is SourceExtensionTypeConstructorBuilder) { |
| return builder; |
| } |
| return null; |
| } |
| |
| @override |
| DartType get declaredRepresentationType => |
| _extensionTypeDeclaration.declaredRepresentationType; |
| |
| @override |
| Iterator<T> fullMemberIterator<T extends Builder>() => |
| new ClassDeclarationMemberIterator<SourceExtensionTypeDeclarationBuilder, |
| T>.full( |
| const _SourceExtensionTypeDeclarationBuilderAugmentationAccess(), |
| this, |
| includeDuplicates: false); |
| |
| @override |
| // Coverage-ignore(suite): Not run. |
| NameIterator<T> fullMemberNameIterator<T extends Builder>() => |
| new ClassDeclarationMemberNameIterator< |
| SourceExtensionTypeDeclarationBuilder, T>( |
| const _SourceExtensionTypeDeclarationBuilderAugmentationAccess(), |
| this, |
| includeDuplicates: false); |
| |
| @override |
| Iterator<T> fullConstructorIterator<T extends MemberBuilder>() => |
| new ClassDeclarationConstructorIterator< |
| SourceExtensionTypeDeclarationBuilder, T>.full( |
| const _SourceExtensionTypeDeclarationBuilderAugmentationAccess(), |
| this, |
| includeDuplicates: false); |
| |
| @override |
| NameIterator<T> fullConstructorNameIterator<T extends MemberBuilder>() => |
| new ClassDeclarationConstructorNameIterator< |
| SourceExtensionTypeDeclarationBuilder, T>( |
| const _SourceExtensionTypeDeclarationBuilderAugmentationAccess(), |
| this, |
| includeDuplicates: false); |
| |
| @override |
| // Coverage-ignore(suite): Not run. |
| bool get isMixinDeclaration => false; |
| |
| @override |
| BodyBuilderContext createBodyBuilderContext( |
| {required bool inOutlineBuildingPhase, |
| required bool inMetadata, |
| required bool inConstFields}) { |
| return new ExtensionTypeBodyBuilderContext(this, |
| inOutlineBuildingPhase: inOutlineBuildingPhase, |
| inMetadata: inMetadata, |
| inConstFields: inConstFields); |
| } |
| |
| /// Return a map whose keys are the supertypes of this |
| /// [SourceExtensionTypeDeclarationBuilder] after expansion of type aliases, |
| /// if any. For each supertype key, the corresponding value is the type alias |
| /// which was unaliased in order to find the supertype, or null if the |
| /// supertype was not aliased. |
| Map<TypeDeclarationBuilder?, TypeAliasBuilder?> computeDirectSupertypes() { |
| final Map<TypeDeclarationBuilder?, TypeAliasBuilder?> result = {}; |
| final List<TypeBuilder>? interfaces = this.interfaceBuilders; |
| if (interfaces != null) { |
| for (int i = 0; i < interfaces.length; i++) { |
| TypeBuilder interface = interfaces[i]; |
| TypeDeclarationBuilder? declarationBuilder = interface.declaration; |
| if (declarationBuilder is TypeAliasBuilder) { |
| TypeDeclarationBuilder? unaliasedDeclaration = |
| interface.computeUnaliasedDeclaration(isUsedAsClass: true); |
| result[unaliasedDeclaration] = declarationBuilder; |
| } else { |
| result[declarationBuilder] = null; |
| } |
| } |
| } |
| return result; |
| } |
| |
| @override |
| // Coverage-ignore(suite): Not run. |
| Iterator<T> localMemberIterator<T extends Builder>() => |
| new ClassDeclarationMemberIterator<SourceExtensionTypeDeclarationBuilder, |
| T>.local(this, includeDuplicates: false); |
| |
| @override |
| // Coverage-ignore(suite): Not run. |
| Iterator<T> localConstructorIterator<T extends MemberBuilder>() => |
| new ClassDeclarationConstructorIterator< |
| SourceExtensionTypeDeclarationBuilder, |
| T>.local(this, includeDuplicates: false); |
| |
| // Coverage-ignore(suite): Not run. |
| /// Returns an iterator the origin extension type declaration and all |
| /// augmentations in application order. |
| Iterator<SourceExtensionTypeDeclarationBuilder> get declarationIterator => |
| new AugmentationIterator<SourceExtensionTypeDeclarationBuilder>( |
| // TODO(johnniwinther): Support augmentations. |
| origin, |
| null); |
| } |
| |
| class _SourceExtensionTypeDeclarationBuilderAugmentationAccess |
| implements |
| ClassDeclarationAugmentationAccess< |
| SourceExtensionTypeDeclarationBuilder> { |
| const _SourceExtensionTypeDeclarationBuilderAugmentationAccess(); |
| |
| @override |
| SourceExtensionTypeDeclarationBuilder getOrigin( |
| SourceExtensionTypeDeclarationBuilder classDeclaration) => |
| classDeclaration.origin; |
| |
| @override |
| Iterable<SourceExtensionTypeDeclarationBuilder>? getAugmentations( |
| SourceExtensionTypeDeclarationBuilder classDeclaration) => |
| null; |
| } |