mirror of
https://github.com/dart-lang/sdk
synced 2024-09-16 04:27:17 +00:00
06763dd539
This adds formatting support for upcoming language features: - Named arguments anywhere - Enhanced enums - "super." parameters It doesn't change the formatting of any existing code, so it should be safe to roll this in without coordinating a pre-built SDK roll. I also went ahead and ran the formatter on the related language tests since before now they couldn't be formatted. (And I incidentally ran the formatter on the other enum tests sitting in the same directory.) Edit: Actually there is one small change to existing formatting: enum declarations will now get a blank line inserted before them. Most hand authored enums already have this so will be unchanged but I see a few diffs when formatting generated code. Change-Id: Icefd9f10bedc589312396cf0ddb8eafc418f8dbf Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/235284 Auto-Submit: Bob Nystrom <rnystrom@google.com> Reviewed-by: Alexander Thomas <athom@google.com> Reviewed-by: William Hesse <whesse@google.com> Commit-Queue: William Hesse <whesse@google.com>
367 lines
11 KiB
Dart
367 lines
11 KiB
Dart
// 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`-constuctor 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);
|
|
}
|