| // Copyright (c) 2021, 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. | |
| // Test new enhanced enum syntax. | |
| import 'package:expect/expect.dart'; | |
| void main() { | |
| Expect.equals(3, EnumPlain.values.length); | |
| Expect.identical(EnumPlain.v1, EnumPlain.values[0]); | |
| Expect.identical(EnumPlain.v2, EnumPlain.values[1]); | |
| Expect.identical(EnumPlain.v3, EnumPlain.values[2]); | |
| Expect.equals(3, EnumPlainTrailingComma.values.length); | |
| Expect.identical(EnumPlainTrailingComma.v1, EnumPlainTrailingComma.values[0]); | |
| Expect.identical(EnumPlainTrailingComma.v2, EnumPlainTrailingComma.values[1]); | |
| Expect.identical(EnumPlainTrailingComma.v3, EnumPlainTrailingComma.values[2]); | |
| Expect.equals(3, EnumNoSemicolon.values.length); | |
| Expect.identical(EnumNoSemicolon.v1, EnumNoSemicolon.values[0]); | |
| Expect.identical(EnumNoSemicolon.v2, EnumNoSemicolon.values[1]); | |
| Expect.identical(EnumNoSemicolon.v3, EnumNoSemicolon.values[2]); | |
| Expect.type<EnumNoSemicolon<num>>(EnumNoSemicolon.v1); | |
| Expect.equals(3, EnumPlainSemicolon.values.length); | |
| Expect.identical(EnumPlainSemicolon.v1, EnumPlainSemicolon.values[0]); | |
| Expect.identical(EnumPlainSemicolon.v2, EnumPlainSemicolon.values[1]); | |
| Expect.identical(EnumPlainSemicolon.v3, EnumPlainSemicolon.values[2]); | |
| Expect.equals(3, EnumPlainTrailingCommaSemicolon.values.length); | |
| Expect.identical( | |
| EnumPlainTrailingCommaSemicolon.v1, | |
| EnumPlainTrailingCommaSemicolon.values[0], | |
| ); | |
| Expect.identical( | |
| EnumPlainTrailingCommaSemicolon.v2, | |
| EnumPlainTrailingCommaSemicolon.values[1], | |
| ); | |
| Expect.identical( | |
| EnumPlainTrailingCommaSemicolon.v3, | |
| EnumPlainTrailingCommaSemicolon.values[2], | |
| ); | |
| Expect.equals(6, EnumAll.values.length); | |
| Expect.identical(EnumAll.v1, EnumAll.values[0]); | |
| Expect.identical(EnumAll.v2, EnumAll.values[1]); | |
| Expect.identical(EnumAll.v3, EnumAll.values[2]); | |
| Expect.identical(EnumAll.v4, EnumAll.values[3]); | |
| Expect.identical(EnumAll.v5, EnumAll.values[4]); | |
| Expect.identical(EnumAll.v6, EnumAll.values[5]); | |
| Expect.equals("unnamed", EnumAll.v1.constructor); | |
| Expect.equals("unnamed", EnumAll.v2.constructor); | |
| Expect.equals("unnamed", EnumAll.v3.constructor); | |
| Expect.equals("named", EnumAll.v4.constructor); | |
| Expect.equals("renamed", EnumAll.v5.constructor); | |
| Expect.equals("unnamed", EnumAll.v6.constructor); | |
| Expect.type<EnumAll<num, num>>(EnumAll.v1); | |
| Expect.type<EnumAll<num, int>>(EnumAll.v2); | |
| Expect.type<EnumAll<int, int>>(EnumAll.v3); | |
| Expect.type<EnumAll<int, int>>(EnumAll.v4); | |
| Expect.type<EnumAll<int, int>>(EnumAll.v5); | |
| Expect.type<EnumAll<num, num>>(EnumAll.v6); | |
| // Access static members. | |
| Expect.identical(EnumAll.v3, EnumAll.sConst); | |
| Expect.identical(EnumAll.v3, EnumAll.sFinal); | |
| Expect.throws(() => EnumAll.sLateFinal); | |
| EnumAll.sLateFinal = EnumAll.v1; | |
| Expect.identical(EnumAll.v1, EnumAll.sLateFinal); | |
| Expect.throws(() => EnumAll.sLateFinal = EnumAll.v1); | |
| Expect.identical(EnumAll.v3, EnumAll.sFinal); | |
| Expect.throws(() => EnumAll.sLateVar); | |
| EnumAll.sLateVar = EnumAll.v1; | |
| Expect.identical(EnumAll.v1, EnumAll.sLateVar); | |
| EnumAll.sLateVar = EnumAll.v3; | |
| Expect.identical(EnumAll.v3, EnumAll.sLateVar); | |
| Expect.identical(EnumAll.v3, EnumAll.sLateVarInit); | |
| Expect.isNull(EnumAll.sVar); | |
| Expect.identical(EnumAll.v3, EnumAll.sVarInit); | |
| Expect.identical(EnumAll.v3, EnumAll.staticGetSet); | |
| EnumAll.staticGetSet = EnumAll.v5; | |
| Expect.equals(42, EnumAll.staticMethod()); | |
| Expect.identical(EnumAll.v3, EnumAll<num, num>.factory(2)); | |
| Expect.identical(EnumAll.v3, EnumAll<num, num>.refactory(2)); | |
| // Access static members through typedef. | |
| Expect.identical(EnumAll.v3, TypeDefAll.sConst); | |
| Expect.identical(EnumAll.v3, TypeDefAll.sFinal); | |
| Expect.identical(EnumAll.v1, TypeDefAll.sLateFinal); | |
| Expect.identical(EnumAll.v3, TypeDefAll.sLateFinalInit); | |
| Expect.identical(EnumAll.v3, TypeDefAll.staticGetSet); | |
| TypeDefAll.staticGetSet = EnumAll.v5; | |
| Expect.equals(42, TypeDefAll.staticMethod()); | |
| Expect.identical(EnumAll.v3, TypeDefAll.factory(2)); | |
| Expect.identical(EnumAll.v3, TypeDefAll.refactory(2)); | |
| // Access instance members. | |
| Expect.equals(0, EnumAll.v1.instanceGetSet); | |
| EnumAll.v1.instanceGetSet = 0.5; | |
| Expect.equals(0, EnumAll.v1.instanceMethod()); | |
| Expect.identical(EnumAll.v4, EnumAll.v3 ^ EnumAll.v2); | |
| Expect.equals( | |
| "EnumAll.v1:EnumMixin<$num>:ObjectMixin:this", | |
| EnumAll.v1.thisAndSuper(), | |
| ); | |
| // Which can reference type parameters. | |
| Expect.isTrue(EnumAll.v2.test(2)); // does `is T` with `T` being `int`. | |
| Expect.isFalse(EnumAll.v2.test(2.5)); | |
| // Including `call`. | |
| Expect.equals(42, EnumAll.v1<int>(42)); | |
| Expect.equals(42, EnumAll.v1(42)); | |
| // Also as tear-off. | |
| Function eaf1 = EnumAll.v1; | |
| Expect.type<T Function<T>(T)>(eaf1); | |
| Function eaf2 = EnumAll.v1<String>; | |
| Expect.type<String Function(String)>(eaf2); | |
| // Instance members shadow extensions. | |
| Expect.equals("not extension", EnumAll.v1.notExtension); | |
| // But you can call extension members if there is no conflict. | |
| Expect.equals("extension", EnumAll.v1.extension); | |
| // The `index` implementation is inherited from the `Enum` implementing | |
| // superclass, and the `toString` implementation is overridden, but | |
| // available via `realToString`. | |
| Expect.equals(0, OverrideEnum.v1.index); | |
| Expect.equals(1, OverrideEnum.v2.index); | |
| Expect.equals(0, OverrideEnum.v1.superIndex); | |
| Expect.equals(1, OverrideEnum.v2.superIndex); | |
| Expect.equals("FakeString", OverrideEnum.v1.toString()); | |
| Expect.equals("FakeString", OverrideEnum.v2.toString()); | |
| Expect.equals("OverrideEnum.v1", OverrideEnum.v1.realToString()); | |
| Expect.equals("OverrideEnum.v2", OverrideEnum.v2.realToString()); | |
| // Enum elements are always distinct, even if their state doesn't differ. | |
| Expect.notIdentical(Canonical.v1, Canonical.v2, "Canonical - type only"); | |
| Expect.notIdentical(Canonical.v2, Canonical.v3, "Canonical - no difference"); | |
| Expect.identical(SelfRefEnum.e1, SelfRefEnum.e2.previous, "SelfRef.prev"); | |
| } | |
| // Original syntax still works, without semicolon after values. | |
| enum EnumPlain { v1, v2, v3 } | |
| // Also with trailing comma. | |
| enum EnumPlainTrailingComma { v1, v2, v3 } | |
| // Also if using type parameters, mixins or interfaces. | |
| // It only matters whether there is something after the values. | |
| enum EnumNoSemicolon<T extends num> with ObjectMixin implements Interface { | |
| v1, | |
| v2, | |
| v3, | |
| } | |
| // Allows semicolon after values, even when not needed. | |
| // Without trailing comma. | |
| enum EnumPlainSemicolon { v1, v2, v3 } | |
| // With trailing comma. | |
| enum EnumPlainTrailingCommaSemicolon { v1, v2, v3 } | |
| // Full syntax, with every possible option. | |
| @EnumAll.v1 | |
| @EnumAll.sConst | |
| enum EnumAll<S extends num, T extends num> | |
| with GenericEnumMixin<T>, ObjectMixin | |
| implements Interface, GenericInterface<S> { | |
| @v1 | |
| @v2 | |
| v1, | |
| @EnumAll.v2 | |
| v2(y: 2), | |
| @sConst | |
| v3<int, int>(y: 2), | |
| v4.named(1, y: 2), | |
| v5<int, int>.renamed(1, y: 2), | |
| v6.new(); | |
| /// Static members. | |
| /// | |
| /// Any kind of static variable. | |
| static const sConst = v3; | |
| static final sFinal = v3; | |
| static late final EnumAll sLateFinal; | |
| static late final sLateFinalInit = v3; | |
| static late EnumAll sLateVar; | |
| static late var sLateVarInit = v3; | |
| static EnumAll? sVar; | |
| static EnumAll sVarInit = v3; | |
| /// Static getters, setters and methods | |
| static EnumAll<int, int> get staticGetSet => v3; | |
| static set staticGetSet(EnumAll<int, int> _) {} | |
| static int staticMethod() => 42; | |
| // Constructors. | |
| // Generative, non-redirecting, unnamed. | |
| const EnumAll({T? y}) | |
| : constructor = "unnamed", | |
| this.x = 0 as S, | |
| y = y ?? (0 as T); | |
| // Generative, non-redirecting, named. | |
| const EnumAll.named(this.x, {T? y, String? constructor}) | |
| : constructor = constructor ?? "named", | |
| y = y ?? (0 as T); | |
| // Generative, redirecting. | |
| const EnumAll.renamed(S x, {T? y}) | |
| : this.named(x, y: y, constructor: "renamed"); | |
| // Factory, non-redirecting. | |
| factory EnumAll.factory(int index) => values[index] as EnumAll<S, T>; | |
| // Factory, redirecting (only to other factory constructor). | |
| factory EnumAll.refactory(int index) = EnumAll<S, T>.factory; | |
| // Cannot have factory constructors redirecting to generative constructors. | |
| // (Nothing can refer to generative constructors except redirecting generative | |
| // constructors and the implicit element creation expressions.) | |
| // Cannot have const factory constructor, because they *must* redirect to | |
| // generative constructors. | |
| // Cannot have `super`-constructor invocations in initializer lists. | |
| // Instance members. | |
| // Instance variables must be final and non-late because of const constructor. | |
| final String constructor; | |
| final S x; | |
| final num y; | |
| // Getters, setters, methods and operators. | |
| S get instanceGetSet => x; | |
| set instanceGetSet(S _) {} | |
| S instanceMethod() => x; | |
| EnumAll<num, num> operator ^(EnumAll<num, num> other) { | |
| var newIndex = index ^ other.index; | |
| if (newIndex > 4) newIndex = 4; | |
| return values[newIndex]; // Can refer to `values`. | |
| } | |
| // Can access `this` and `super` in an instance method. | |
| String thisAndSuper() => "${super.toString()}:${this.toString()}"; | |
| // Can be callable. | |
| T call<T>(T value) => value; | |
| // Can have an `index` setter. | |
| set index(int value) {} | |
| // Instance members shadow extensions. | |
| String get notExtension => "not extension"; | |
| String toString() => "this"; | |
| } | |
| extension EnumAllExtension on EnumAll { | |
| String get notExtension { | |
| Expect.fail("Unreachable"); | |
| return "not"; | |
| } | |
| String get extension => "extension"; | |
| } | |
| typedef TypeDefAll = EnumAll<num, num>; | |
| // Can have no unnamed constructor. | |
| enum EnumNoUnnamedConstructor { | |
| v1.named(1), | |
| v2.named(2); | |
| final int x; | |
| const EnumNoUnnamedConstructor.named(this.x); | |
| } | |
| enum NewNamedConstructor { | |
| v1; | |
| const NewNamedConstructor.new(); | |
| } | |
| // Can have an unnamed factory constructor. | |
| enum EnumFactoryUnnamedConstructor { | |
| v1.named(1), | |
| v2.named(2); | |
| final int x; | |
| factory EnumFactoryUnnamedConstructor() => v1; | |
| const EnumFactoryUnnamedConstructor.named(this.x); | |
| } | |
| // Elements which do not differ in public state are still different. | |
| // Ditto if only differing in type arguments. | |
| enum Canonical<T> { | |
| v1<int>(1), | |
| v2<num>(1), | |
| v3<num>(1); | |
| final T value; | |
| const Canonical(this.value); | |
| } | |
| // Both `toString` and `index` are inherited from superclass. | |
| enum OverrideEnum { | |
| v1, | |
| v2; | |
| // Cannot override index | |
| int get superIndex => super.index; | |
| String toString() => "FakeString"; | |
| String realToString() => super.toString(); | |
| } | |
| // An enum value expression *can* reference another enum value. | |
| enum SelfRefEnum { | |
| e1(null), | |
| e2(e1); | |
| final SelfRefEnum? previous; | |
| const SelfRefEnum(this.previous); | |
| } | |
| // -------------------------------------------------------------------- | |
| // Helper declarations | |
| mixin ObjectMixin on Object { | |
| String toString() => "${super.toString()}:ObjectMixin"; | |
| } | |
| mixin EnumMixin on Enum { | |
| String toString() => "${super.toString()}:EnumMixin"; | |
| } | |
| mixin GenericObjectMixin<T> on Object { | |
| bool test(Object o) => o is T; | |
| String toString() => "${super.toString()}:ObjectMixin<$T>"; | |
| } | |
| mixin GenericEnumMixin<T> on Enum { | |
| bool test(Object o) => o is T; | |
| String toString() => "${super.toString()}:EnumMixin<$T>"; | |
| } | |
| abstract class Interface {} | |
| abstract class GenericInterface<T> { | |
| // Implemented by mixins. | |
| bool test(Object o); | |
| } |