[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);
+}