[tests] Enum shorthand language tests - simple identifier and == only.
This CL adds language tests for the simple identifier variation of enum shorthands. In these tests, we are testing prefix identifiers with a single simple identifier.
For example, `Decoration(fit: BoxFit.cover)` where `fit` has the type `BoxFit?`, we would be able to turn this into `Decoration(fit: .cover)` with enum shorthands.
The tests also test erroneous ways that someone can use the `==` operator like the shorthand not being on the RHS.
Bug: https://github.com/dart-lang/sdk/issues/57038
Change-Id: I03610db2a683e9c2cdf8c12a99c7808171377e3a
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/393606
Commit-Queue: Kallen Tu <kallentu@google.com>
Reviewed-by: Erik Ernst <eernst@google.com>
diff --git a/tests/language/enum_shorthands/enum_shorthand_helper.dart b/tests/language/enum_shorthands/enum_shorthand_helper.dart
new file mode 100644
index 0000000..5efc67b
--- /dev/null
+++ b/tests/language/enum_shorthands/enum_shorthand_helper.dart
@@ -0,0 +1,25 @@
+// Copyright (c) 2024, 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.
+
+// Common classes and enums for testing enum shorthands.
+
+// SharedOptions=--enable-experiment=enum-shorthands
+
+enum Color { blue, red, green }
+
+class Integer {
+ static Integer get one => Integer(1);
+ static const Integer constOne = const Integer._(1);
+ static const Integer constTwo = const Integer._(2);
+ final int integer;
+ Integer(this.integer);
+ const Integer._(this.integer);
+}
+
+extension type IntegerExt(int integer) {
+ static IntegerExt get one => IntegerExt(1);
+ static const IntegerExt constOne = const IntegerExt._(1);
+ static const IntegerExt constTwo = const IntegerExt._(2);
+ const IntegerExt._(this.integer);
+}
diff --git a/tests/language/enum_shorthands/equality/equality_error_test.dart b/tests/language/enum_shorthands/equality/equality_error_test.dart
new file mode 100644
index 0000000..c6bcd9c
--- /dev/null
+++ b/tests/language/enum_shorthands/equality/equality_error_test.dart
@@ -0,0 +1,358 @@
+// Copyright (c) 2024, 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.
+
+// Testing erroneous ways of using shorthands with the `==` and `!=` operators.
+
+// SharedOptions=--enable-experiment=enum-shorthands
+
+import '../enum_shorthand_helper.dart';
+
+class ConstConstructorAssert {
+ const ConstConstructorAssert.blue(Color color)
+ : assert(.blue == color);
+ // ^
+ // [analyzer] unspecified
+ // [cfe] unspecified
+
+ const ConstConstructorAssert.notBlue(Color color)
+ : assert(.blue != color);
+ // ^
+ // [analyzer] unspecified
+ // [cfe] unspecified
+
+ const ConstConstructorAssert.one(Integer integer)
+ : assert(.constOne == integer);
+ // ^
+ // [analyzer] unspecified
+ // [cfe] unspecified
+
+ const ConstConstructorAssert.notOne(Integer integer)
+ : assert(.constOne != integer);
+ // ^
+ // [analyzer] unspecified
+ // [cfe] unspecified
+
+ const ConstConstructorAssert.oneExt(IntegerExt integer)
+ : assert(.constOne == integer);
+ // ^
+ // [analyzer] unspecified
+ // [cfe] unspecified
+
+ const ConstConstructorAssert.notOneExt(IntegerExt integer)
+ : assert(.constOne != integer);
+ // ^
+ // [analyzer] unspecified
+ // [cfe] unspecified
+}
+
+void notSymmetrical(Color color, Integer integer, IntegerExt integerExt) {
+ const bool symBlueEq = .blue == color;
+ // ^
+ // [analyzer] unspecified
+ // [cfe] unspecified
+
+ const bool symBlueNeq = .blue != color;
+ // ^
+ // [analyzer] unspecified
+ // [cfe] unspecified
+
+ const bool symOneEq = .one == integer;
+ // ^
+ // [analyzer] unspecified
+ // [cfe] unspecified
+
+ const bool symOneNeq = .one != integer;
+ // ^
+ // [analyzer] unspecified
+ // [cfe] unspecified
+
+ const bool symOneExtEq = .one == integerExt;
+ // ^
+ // [analyzer] unspecified
+ // [cfe] unspecified
+
+ const bool symOneExtNeq = .one != integerExt;
+ // ^
+ // [analyzer] unspecified
+ // [cfe] unspecified
+
+ if (.blue == color) print('not ok');
+ // ^
+ // [analyzer] unspecified
+ // [cfe] unspecified
+
+ if (.blue != color) print('not ok');
+ // ^
+ // [analyzer] unspecified
+ // [cfe] unspecified
+
+ if (.one == integer) print('not ok');
+ // ^
+ // [analyzer] unspecified
+ // [cfe] unspecified
+
+ if (.one != integer) print('not ok');
+ // ^
+ // [analyzer] unspecified
+ // [cfe] unspecified
+
+ if (.one == integerExt) print('not ok');
+ // ^
+ // [analyzer] unspecified
+ // [cfe] unspecified
+
+ if (.one != integerExt) print('not ok');
+ // ^
+ // [analyzer] unspecified
+ // [cfe] unspecified
+}
+
+void rhsNeedsToBeShorthand(Color color, Integer integer, IntegerExt integerExt, bool condition) {
+ const Color constColor = Color.red;
+ const Object obj = true;
+ const bool constCondition = obj as bool;
+
+ const bool rhsColorEq = constColor == (constCondition ? .red : .green);
+ // ^
+ // [analyzer] unspecified
+ // [cfe] unspecified
+
+ const bool rhsColorNeq = constColor != (constCondition ? .red : .green);
+ // ^
+ // [analyzer] unspecified
+ // [cfe] unspecified
+
+ if (color == (condition ? .red : .green)) print('not ok');
+ // ^
+ // [analyzer] unspecified
+ // [cfe] unspecified
+
+ if (color != (condition ? .red : .green)) print('not ok');
+ // ^
+ // [analyzer] unspecified
+ // [cfe] unspecified
+
+ if (color case == (constCondition ? .red : .green)) {
+ // ^
+ // [analyzer] unspecified
+ // [cfe] unspecified
+ print('not ok');
+ }
+
+ if (color case != (constCondition ? .red : .green)) {
+ // ^
+ // [analyzer] unspecified
+ // [cfe] unspecified
+ print('not ok');
+ }
+
+ const Integer constInteger = Integer.constOne;
+ const bool rhsIntegerEq = constInteger == (constCondition ? .constOne : .constTwo);
+ // ^
+ // [analyzer] unspecified
+ // [cfe] unspecified
+
+ const bool rhsIntegerNeq = constInteger != (constCondition ? .constOne : .constTwo);
+ // ^
+ // [analyzer] unspecified
+ // [cfe] unspecified
+
+ if (integer == (condition ? .constOne : .constTwo)) {
+ // ^
+ // [analyzer] unspecified
+ // [cfe] unspecified
+ print('not ok');
+ }
+
+ if (integer != (condition ? .constOne : .constTwo)) {
+ // ^
+ // [analyzer] unspecified
+ // [cfe] unspecified
+ print('not ok');
+ }
+
+ if (integer case == (constCondition ? .constOne : .constTwo)) {
+ // ^
+ // [analyzer] unspecified
+ // [cfe] unspecified
+ print('not ok');
+ }
+
+ if (integer case != (constCondition ? .constOne : .constTwo)) {
+ // ^
+ // [analyzer] unspecified
+ // [cfe] unspecified
+ print('not ok');
+ }
+
+ const IntegerExt constIntegerExt = IntegerExt.constOne;
+ const bool rhsIntegerExtEq = constIntegerExt == (constCondition ? .constOne : .constTwo);
+ // ^
+ // [analyzer] unspecified
+ // [cfe] unspecified
+
+ const bool rhsIntegerExtNeq = constIntegerExt != (constCondition ? .constOne : .constTwo);
+ // ^
+ // [analyzer] unspecified
+ // [cfe] unspecified
+
+ if (integerExt == (condition ? .constOne : .constTwo)) {
+ // ^
+ // [analyzer] unspecified
+ // [cfe] unspecified
+ print('not ok');
+ }
+
+ if (integerExt != (condition ? .constOne : .constTwo)) {
+ // ^
+ // [analyzer] unspecified
+ // [cfe] unspecified
+ print('not ok');
+ }
+
+ if (integerExt case == (constCondition ? .constOne : .constTwo)) {
+ // ^
+ // [analyzer] unspecified
+ // [cfe] unspecified
+ print('not ok');
+ }
+
+ if (integerExt case != (constCondition ? .constOne : .constTwo)) {
+ // ^
+ // [analyzer] unspecified
+ // [cfe] unspecified
+ print('not ok');
+ }
+}
+
+void objectContextType(Color color, Integer integer, IntegerExt integerExt) {
+ const Color constColor = Color.red;
+ const bool contextTypeColorEq = (constColor as Object) == .blue;
+ // ^
+ // [analyzer] unspecified
+ // [cfe] unspecified
+
+ const bool contextTypeColorNeq = (constColor as Object) != .blue;
+ // ^
+ // [analyzer] unspecified
+ // [cfe] unspecified
+
+ if ((color as Object) == .blue) print('not ok');
+ // ^
+ // [analyzer] unspecified
+ // [cfe] unspecified
+
+ if ((color as Object) case == .blue) print('not ok');
+ // ^
+ // [analyzer] unspecified
+ // [cfe] unspecified
+
+ if ((color as Object) != .blue) print('not ok');
+ // ^
+ // [analyzer] unspecified
+ // [cfe] unspecified
+
+ if ((color as Object) case != .blue) print('not ok');
+ // ^
+ // [analyzer] unspecified
+ // [cfe] unspecified
+
+ const Integer constInteger = Integer.constOne;
+ const bool contextTypeIntegerEq = (constInteger as Object) == .constTwo;
+ // ^
+ // [analyzer] unspecified
+ // [cfe] unspecified
+
+ const bool contextTypeIntegerNeq = (constInteger as Object) != .constTwo;
+ // ^
+ // [analyzer] unspecified
+ // [cfe] unspecified
+
+ if ((integer as Object) == .one) print('not ok');
+ // ^
+ // [analyzer] unspecified
+ // [cfe] unspecified
+
+ if ((integer as Object) case == .constOne) print('not ok');
+ // ^
+ // [analyzer] unspecified
+ // [cfe] unspecified
+
+ if ((integer as Object) != .one) print('not ok');
+ // ^
+ // [analyzer] unspecified
+ // [cfe] unspecified
+
+ if ((integer as Object) case != .constOne) print('not ok');
+ // ^
+ // [analyzer] unspecified
+ // [cfe] unspecified
+
+ const IntegerExt constIntegerExt = IntegerExt.constOne;
+ const bool contextTypeIntegerExtEq = (constIntegerExt as Object) == .constTwo;
+ // ^
+ // [analyzer] unspecified
+ // [cfe] unspecified
+
+ const bool contextTypeIntegerExtNeq = (constIntegerExt as Object) != .constTwo;
+ // ^
+ // [analyzer] unspecified
+ // [cfe] unspecified
+
+ if ((integerExt as Object) == .one) print('not ok');
+ // ^
+ // [analyzer] unspecified
+ // [cfe] unspecified
+
+ if ((integerExt as Object) case == .constOne) print('not ok');
+ // ^
+ // [analyzer] unspecified
+ // [cfe] unspecified
+
+ if ((integerExt as Object) != .one) print('not ok');
+ // ^
+ // [analyzer] unspecified
+ // [cfe] unspecified
+
+ if ((integerExt as Object) case != .constOne) print('not ok');
+ // ^
+ // [analyzer] unspecified
+ // [cfe] unspecified
+}
+
+void typeParameterContext<C extends Color, T extends Object>(C color, T value) {
+ if (color == .red) print('not ok');
+ // ^
+ // [analyzer] unspecified
+ // [cfe] unspecified
+ if (value is Color) {
+ if (value == .red) print('not ok');
+ // ^
+ // [analyzer] unspecified
+ // [cfe] unspecified
+ }
+}
+
+void main() {
+ Color color = .blue;
+ Integer integer = .one;
+ IntegerExt integerExt = .one;
+
+ notSymmetrical(color, integer, integerExt);
+ rhsNeedsToBeShorthand(color, integer, integerExt, true);
+ rhsNeedsToBeShorthand(color, integer, integerExt, false);
+ objectContextType(color, integer, integerExt);
+
+ typeParameterContext(color, integer);
+ typeParameterContext(color, Color.red);
+
+ // Test the constant evaluation for enum shorthands in const constructor
+ // asserts.
+ const ConstConstructorAssert.blue(Color.blue);
+ const ConstConstructorAssert.notBlue(Color.red);
+ const ConstConstructorAssert.one(Integer.constOne);
+ const ConstConstructorAssert.notOne(Integer.constTwo);
+ const ConstConstructorAssert.oneExt(IntegerExt.constOne);
+ const ConstConstructorAssert.notOneExt(IntegerExt.constTwo);
+}
diff --git a/tests/language/enum_shorthands/equality/equality_test.dart b/tests/language/enum_shorthands/equality/equality_test.dart
new file mode 100644
index 0000000..855c7a2
--- /dev/null
+++ b/tests/language/enum_shorthands/equality/equality_test.dart
@@ -0,0 +1,79 @@
+// Copyright (c) 2024, 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.
+
+// Testing the == and != behaviour for enum shorthands.
+
+// SharedOptions=--enable-experiment=enum-shorthands
+
+import '../enum_shorthand_helper.dart';
+
+class ConstConstructorAssert {
+ const ConstConstructorAssert.blue(Color color)
+ : assert(color == .blue);
+
+ const ConstConstructorAssert.notBlue(Color color)
+ : assert(color == .blue);
+
+ const ConstConstructorAssert.one(Integer integer)
+ : assert(integer == .constOne);
+
+ const ConstConstructorAssert.notOne(Integer integer)
+ : assert(integer == .constOne);
+
+ const ConstConstructorAssert.oneExt(IntegerExt integer)
+ : assert(integer == .constOne);
+
+ const ConstConstructorAssert.notOneExt(IntegerExt integer)
+ : assert(integer == .constOne);
+}
+
+void main() {
+ // Enum
+ Color color = .blue;
+ const Color constColor = .blue;
+
+ const bool constColorEq = constColor == .blue;
+ const bool constColorNeq = constColor != .blue;
+
+ if (color == .blue) print('ok');
+ if (color case == .blue) print('ok');
+
+ if (color != .blue) print('ok');
+ if (color case != .blue) print('ok');
+
+ // Class
+ Integer integer = .one;
+ const Integer constInteger = .one;
+
+ const bool constIntegerEq = constInteger == .constOne;
+ const bool constIntegerNeq = constInteger != .constOne;
+
+ if (integer == .one) print('ok');
+ if (integer case == .constOne) print('ok');
+
+ if (integer != .one) print('ok');
+ if (integer case != .constOne) print('ok');
+
+ // Extension type
+ IntegerExt integerExt = .one;
+ const IntegerExt constIntegerExt = .constOne;
+
+ const bool constIntegerExtEq = constIntegerExt == .constOne;
+ const bool constIntegerExtNeq = constIntegerExt != .constOne;
+
+ if (integerExt == .one) print('ok');
+ if (integerExt case == .constOne) print('ok');
+
+ if (integerExt != .one) print('ok');
+ if (integerExt case != .constOne) print('ok');
+
+ // Test the constant evaluation for enum shorthands in const constructor
+ // asserts.
+ const ConstConstructorAssert.blue(Color.blue);
+ const ConstConstructorAssert.notBlue(Color.red);
+ const ConstConstructorAssert.one(Integer.constOne);
+ const ConstConstructorAssert.notOne(Integer.constTwo);
+ const ConstConstructorAssert.oneExt(IntegerExt.constOne);
+ const ConstConstructorAssert.notOneExt(IntegerExt.constTwo);
+}
diff --git a/tests/language/enum_shorthands/feature_disabled_error_test.dart b/tests/language/enum_shorthands/feature_disabled_error_test.dart
new file mode 100644
index 0000000..01d08f2
--- /dev/null
+++ b/tests/language/enum_shorthands/feature_disabled_error_test.dart
@@ -0,0 +1,16 @@
+// Copyright (c) 2024, 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.
+
+// Enum shorthands are not enabled in versions before release.
+
+// @dart=3.6
+
+import 'enum_shorthand_helper.dart';
+
+void main() {
+ Color color = .blue;
+ // ^
+ // [analyzer] SYNTACTIC_ERROR.MISSING_IDENTIFIER
+ // [cfe] Expected an identifier, but got '.'.
+}
diff --git a/tests/language/enum_shorthands/feature_enabled_error_test.dart b/tests/language/enum_shorthands/feature_enabled_error_test.dart
new file mode 100644
index 0000000..7ac8abe
--- /dev/null
+++ b/tests/language/enum_shorthands/feature_enabled_error_test.dart
@@ -0,0 +1,15 @@
+// Copyright (c) 2024, 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.
+
+// Enum shorthands are enabled by default. This should be an error as it's not
+// currently enabled by default.
+
+import 'enum_shorthand_helper.dart';
+
+void main() {
+ Color color = .blue;
+ // ^
+ // [analyzer] SYNTACTIC_ERROR.MISSING_IDENTIFIER
+ // [cfe] Expected an identifier, but got '.'.
+}
diff --git a/tests/language/enum_shorthands/simple/simple_identifier_error_test.dart b/tests/language/enum_shorthands/simple/simple_identifier_error_test.dart
new file mode 100644
index 0000000..cd66041
--- /dev/null
+++ b/tests/language/enum_shorthands/simple/simple_identifier_error_test.dart
@@ -0,0 +1,32 @@
+// Copyright (c) 2024, 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.
+
+// Erroneous ways to use shorthands for simple identifiers and const simple
+// identifiers.
+
+// SharedOptions=--enable-experiment=enum-shorthands
+
+import '../enum_shorthand_helper.dart';
+
+void main() {
+ var color = .blue;
+ // ^
+ // [analyzer] unspecified
+ // [cfe] unspecified
+
+ const constColor = .blue;
+ // ^
+ // [analyzer] unspecified
+ // [cfe] unspecified
+
+ var integer = .one;
+ // ^
+ // [analyzer] unspecified
+ // [cfe] unspecified
+
+ const constInteger = .one;
+ // ^
+ // [analyzer] unspecified
+ // [cfe] unspecified
+}
diff --git a/tests/language/enum_shorthands/simple/simple_identifier_test.dart b/tests/language/enum_shorthands/simple/simple_identifier_test.dart
new file mode 100644
index 0000000..d1ecb95
--- /dev/null
+++ b/tests/language/enum_shorthands/simple/simple_identifier_test.dart
@@ -0,0 +1,61 @@
+// Copyright (c) 2024, 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.
+
+// Shorthands for simple identifiers and const simple identifiers.
+
+// SharedOptions=--enable-experiment=enum-shorthands
+
+import '../enum_shorthand_helper.dart';
+
+class ColorContext {
+ final Color? color;
+ ColorContext(this.color);
+ ColorContext.named({this.color});
+ ColorContext.optional([this.color]);
+}
+
+class IntegerContext {
+ final Integer? integer;
+ IntegerContext(this.integer);
+ IntegerContext.named({this.integer});
+ IntegerContext.optional([this.integer]);
+}
+
+class IntegerExtContext {
+ final IntegerExt? integer;
+ IntegerExtContext(this.integer);
+ IntegerExtContext.named({this.integer});
+ IntegerExtContext.optional([this.integer]);
+}
+
+void main() {
+ // Enum
+ Color color = .blue;
+ const Color constColor = .blue;
+ var colorContextPositional = ColorContext(.blue);
+ var colorContextNamed = ColorContext.named(color: .blue);
+ var colorContextOptional = ColorContext.optional(.blue);
+ switch (color) {
+ case .blue:
+ print('blue');
+ case .red:
+ print('red');
+ case .green:
+ print('green');
+ }
+
+ // Class
+ Integer integer = .one;
+ const Integer constInteger = .constOne;
+ var integerContextPositional = IntegerContext(.one);
+ var integerContextNamed = IntegerContext.named(integer: .one);
+ var integerContextOptional = IntegerContext.optional(.one);
+
+ // Extension type
+ IntegerExt integerExt = .one;
+ const IntegerExt constIntegerExt = .constOne;
+ var integerExtContextPositional = IntegerExtContext(.one);
+ var integerExtContextNamed = IntegerExtContext.named(integer: .one);
+ var integerExtContextOptional = IntegerExtContext.optional(.one);
+}