[cfe] Handle static gets and tearoffs in dot shorthands
Resolve static getters and tear-offs in dot shorthands.
Method and constructor invocations will come in a later CL. Also, looking to update the parser handling in a future CL, but we'll work incrementally.
Bug: https://github.com/dart-lang/sdk/issues/59758
Change-Id: I15c9eb7e531975ea19d496a03ac4b666fa15a04e
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/411940
Reviewed-by: Chloe Stefantsova <cstefantsova@google.com>
Commit-Queue: Kallen Tu <kallentu@google.com>
diff --git a/pkg/front_end/lib/src/kernel/body_builder.dart b/pkg/front_end/lib/src/kernel/body_builder.dart
index 661228f..323fff8 100644
--- a/pkg/front_end/lib/src/kernel/body_builder.dart
+++ b/pkg/front_end/lib/src/kernel/body_builder.dart
@@ -9976,23 +9976,29 @@
}
@override
- // Coverage-ignore(suite): Not run.
void handleDotShorthandContext(Token token) {
debugEvent("DotShorthandContext");
if (!libraryFeatures.dotShorthands.isEnabled) {
+ // Coverage-ignore-block(suite): Not run.
addProblem(
templateExperimentNotEnabledOffByDefault
.withArguments(ExperimentalFlag.dotShorthands.name),
token.offset,
token.length);
+ return;
}
+
+ // TODO(kallentu): Possibly could be ProblemBuilder? Testing needed.
+ assert(checkState(token, [ValueKinds.Expression]));
+ Expression value = pop() as Expression;
+ push(forest.createDotShorthandContext(token.charOffset, value));
}
@override
- // Coverage-ignore(suite): Not run.
void handleDotShorthandHead(Token token) {
debugEvent("DotShorthandHead");
if (!libraryFeatures.dotShorthands.isEnabled) {
+ // Coverage-ignore-block(suite): Not run.
addProblem(
templateExperimentNotEnabledOffByDefault
.withArguments(ExperimentalFlag.dotShorthands.name),
@@ -10001,9 +10007,20 @@
// Recovery, avoid crashing with an extra selector.
pop();
+ return;
}
- // TODO(kallentu): Handle dot shorthands.
+ Object? selector = pop();
+ if (libraryFeatures.dotShorthands.isEnabled && selector is Selector) {
+ // TODO(kallentu): Remove this once we have more of the dot shorthands
+ // implementation complete.
+ pop(); // ParserGeneratorError
+
+ // TODO(kallentu): Handle invocations.
+
+ push(forest.createDotShorthandPropertyGet(
+ offsetForToken(token), selector.name));
+ }
}
}
diff --git a/pkg/front_end/lib/src/kernel/forest.dart b/pkg/front_end/lib/src/kernel/forest.dart
index 63e2020..da00ea7 100644
--- a/pkg/front_end/lib/src/kernel/forest.dart
+++ b/pkg/front_end/lib/src/kernel/forest.dart
@@ -928,13 +928,11 @@
..fileOffset = fileOffset;
}
- // Coverage-ignore(suite): Not run.
DotShorthand createDotShorthandContext(
int fileOffset, Expression innerExpression) {
return new DotShorthand(innerExpression)..fileOffset = fileOffset;
}
- // Coverage-ignore(suite): Not run.
DotShorthandPropertyGet createDotShorthandPropertyGet(
int fileOffset, Name name) {
return new DotShorthandPropertyGet(name)..fileOffset = fileOffset;
diff --git a/pkg/front_end/lib/src/kernel/hierarchy/members_builder.dart b/pkg/front_end/lib/src/kernel/hierarchy/members_builder.dart
index 31dc8bc..18a8363 100644
--- a/pkg/front_end/lib/src/kernel/hierarchy/members_builder.dart
+++ b/pkg/front_end/lib/src/kernel/hierarchy/members_builder.dart
@@ -202,6 +202,10 @@
return getNodeFromClass(cls).getDispatchTarget(name, setter);
}
+ Member? getStaticMember(Class cls, Name name, {bool setter = false}) {
+ return getNodeFromClass(cls).getStaticMember(name, setter)?.getMember(this);
+ }
+
static ClassMembersBuilder build(
ClassHierarchyBuilder hierarchyBuilder,
List<ClassBuilder> classes,
diff --git a/pkg/front_end/lib/src/kernel/hierarchy/members_node.dart b/pkg/front_end/lib/src/kernel/hierarchy/members_node.dart
index 46b3e03..a240bff 100644
--- a/pkg/front_end/lib/src/kernel/hierarchy/members_node.dart
+++ b/pkg/front_end/lib/src/kernel/hierarchy/members_node.dart
@@ -1187,6 +1187,16 @@
}
return result;
}
+
+ ClassMember? getStaticMember(Name name, bool isSetter) {
+ ClassMember? result = classMemberMap[name];
+ if (result == null) {
+ return null;
+ } else if (result.isStatic) {
+ return result;
+ }
+ return null;
+ }
}
// Coverage-ignore(suite): Not run.
diff --git a/pkg/front_end/lib/src/kernel/internal_ast.dart b/pkg/front_end/lib/src/kernel/internal_ast.dart
index 825bea8..e88c9d5 100644
--- a/pkg/front_end/lib/src/kernel/internal_ast.dart
+++ b/pkg/front_end/lib/src/kernel/internal_ast.dart
@@ -3222,7 +3222,6 @@
'ExtensionTypeRepresentationFieldInitializer(${toStringInternal()})';
}
-// Coverage-ignore(suite): Not run.
/// Internal expression for a dot shorthand.
///
/// This node wraps around the [innerExpression] and indicates to the
@@ -3245,12 +3244,12 @@
}
@override
+ // Coverage-ignore(suite): Not run.
void toTextInternal(AstPrinter printer) {
printer.writeExpression(innerExpression);
}
}
-// Coverage-ignore(suite): Not run.
/// Internal expression for a dot shorthand head with no arguments.
/// (e.g. `.zero`).
///
@@ -3272,6 +3271,7 @@
}
@override
+ // Coverage-ignore(suite): Not run.
void toTextInternal(AstPrinter printer) {
printer.write('.');
printer.writeName(name);
diff --git a/pkg/front_end/lib/src/type_inference/inference_visitor.dart b/pkg/front_end/lib/src/type_inference/inference_visitor.dart
index a5d5249..ab40cdf 100644
--- a/pkg/front_end/lib/src/type_inference/inference_visitor.dart
+++ b/pkg/front_end/lib/src/type_inference/inference_visitor.dart
@@ -12104,7 +12104,6 @@
return _unhandledStatement(node);
}
- // Coverage-ignore(suite): Not run.
ExpressionInferenceResult visitDotShorthand(
DotShorthand node, DartType typeContext) {
DartType rewrittenType = analyzeDotShorthand(
@@ -12114,11 +12113,34 @@
return new ExpressionInferenceResult(rewrittenType, rewrittenExpr);
}
- // Coverage-ignore(suite): Not run.
ExpressionInferenceResult visitDotShorthandPropertyGet(
DotShorthandPropertyGet node, DartType typeContext) {
- // TODO(kallentu): Implementation needed for dot shorthands.
- return _unhandledExpression(node, typeContext);
+ // Use the previously cached context type to determine the declaration
+ // member that we're trying to find.
+ DartType cachedContext = getDotShorthandContext().unwrapTypeSchemaView();
+ Member? member = findInterfaceMember(
+ cachedContext, node.name, node.fileOffset,
+ includeExtensionMethods: false,
+ isSetter: false,
+ isDotShorthand: true)
+ .member;
+
+ ExpressionInferenceResult expressionInferenceResult;
+ if (member == null) {
+ // TODO(kallentu): This is temporary. Build a problem with an error
+ // specific to not being able to find a member named [node.name].
+ throw 'Error: Cannot find dot shorthand member.';
+ } else if (member is Procedure && !member.isGetter) {
+ // Tearoff like `Object.new`;
+ expressionInferenceResult =
+ inferExpression(new StaticTearOff(member), cachedContext);
+ } else {
+ expressionInferenceResult =
+ inferExpression(new StaticGet(member), cachedContext);
+ }
+
+ flowAnalysis.forwardExpression(expressionInferenceResult.expression, node);
+ return expressionInferenceResult;
}
}
diff --git a/pkg/front_end/lib/src/type_inference/inference_visitor_base.dart b/pkg/front_end/lib/src/type_inference/inference_visitor_base.dart
index 2da0858..200357e 100644
--- a/pkg/front_end/lib/src/type_inference/inference_visitor_base.dart
+++ b/pkg/front_end/lib/src/type_inference/inference_visitor_base.dart
@@ -1181,7 +1181,8 @@
DartType receiverType, Name name, int fileOffset,
{required bool isSetter,
bool instrumented = true,
- bool includeExtensionMethods = false}) {
+ bool includeExtensionMethods = false,
+ bool isDotShorthand = false}) {
assert(isKnown(receiverType));
DartType receiverBound = receiverType.nonTypeParameterBound;
@@ -1200,6 +1201,7 @@
classNode: classNode,
receiverBound: receiverBound,
hasNonObjectMemberAccess: hasNonObjectMemberAccess,
+ isDotShorthand: isDotShorthand,
isSetter: isSetter,
fileOffset: fileOffset);
@@ -3732,6 +3734,12 @@
return TypeInferenceEngine.resolveInferenceNode(member, hierarchyBuilder);
}
+ Member? _getStaticMember(Class class_, Name name, bool setter) {
+ Member? member =
+ engine.membersBuilder.getStaticMember(class_, name, setter: setter);
+ return TypeInferenceEngine.resolveInferenceNode(member, hierarchyBuilder);
+ }
+
ClassMember? _getExtensionTypeMember(
ExtensionTypeDeclaration extensionTypeDeclaration,
Name name,
@@ -4617,6 +4625,7 @@
final DartType receiverBound;
final Class classNode;
final bool hasNonObjectMemberAccess;
+ final bool isDotShorthand;
final bool isSetter;
final int fileOffset;
@@ -4626,6 +4635,7 @@
required this.receiverBound,
required this.classNode,
required this.hasNonObjectMemberAccess,
+ required this.isDotShorthand,
required this.isSetter,
required this.fileOffset});
@@ -4717,8 +4727,9 @@
}
ObjectAccessTarget? target;
- Member? interfaceMember =
- visitor._getInterfaceMember(classNode, name, isSetter);
+ Member? interfaceMember = isDotShorthand
+ ? visitor._getStaticMember(classNode, name, isSetter)
+ : visitor._getInterfaceMember(classNode, name, isSetter);
if (interfaceMember != null) {
target = new ObjectAccessTarget.interfaceMember(
receiverType, interfaceMember,
diff --git a/pkg/front_end/test/coverage_suite_expected.dart b/pkg/front_end/test/coverage_suite_expected.dart
index 49848ae..3926175 100644
--- a/pkg/front_end/test/coverage_suite_expected.dart
+++ b/pkg/front_end/test/coverage_suite_expected.dart
@@ -590,7 +590,7 @@
),
// 100.0%.
"package:front_end/src/kernel/body_builder.dart": (
- hitCount: 7188,
+ hitCount: 7216,
missCount: 0,
),
// 100.0%.
@@ -660,7 +660,7 @@
),
// 100.0%.
"package:front_end/src/kernel/forest.dart": (
- hitCount: 396,
+ hitCount: 402,
missCount: 0,
),
// 100.0%.
@@ -695,12 +695,12 @@
),
// 100.0%.
"package:front_end/src/kernel/hierarchy/members_builder.dart": (
- hitCount: 129,
+ hitCount: 133,
missCount: 0,
),
// 100.0%.
"package:front_end/src/kernel/hierarchy/members_node.dart": (
- hitCount: 1106,
+ hitCount: 1110,
missCount: 0,
),
// 100.0%.
@@ -720,7 +720,7 @@
),
// 100.0%.
"package:front_end/src/kernel/internal_ast.dart": (
- hitCount: 565,
+ hitCount: 571,
missCount: 0,
),
// 100.0%.
@@ -976,12 +976,12 @@
),
// 100.0%.
"package:front_end/src/type_inference/inference_visitor.dart": (
- hitCount: 8260,
+ hitCount: 8283,
missCount: 0,
),
// 100.0%.
"package:front_end/src/type_inference/inference_visitor_base.dart": (
- hitCount: 2442,
+ hitCount: 2451,
missCount: 0,
),
// 100.0%.
diff --git a/pkg/front_end/testcases/dot_shorthands/folder.options b/pkg/front_end/testcases/dot_shorthands/folder.options
new file mode 100644
index 0000000..88c4c4e
--- /dev/null
+++ b/pkg/front_end/testcases/dot_shorthands/folder.options
@@ -0,0 +1 @@
+--enable-experiment=dot-shorthands
diff --git a/pkg/front_end/testcases/dot_shorthands/simple_class.dart b/pkg/front_end/testcases/dot_shorthands/simple_class.dart
new file mode 100644
index 0000000..c568cf0
--- /dev/null
+++ b/pkg/front_end/testcases/dot_shorthands/simple_class.dart
@@ -0,0 +1,13 @@
+// Copyright (c) 2025, 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.
+
+class Color {
+ final int x;
+ static Color get red => Color(1);
+ Color(this.x);
+}
+
+void main() {
+ Color c = .red;
+}
diff --git a/pkg/front_end/testcases/dot_shorthands/simple_class.dart.strong.expect b/pkg/front_end/testcases/dot_shorthands/simple_class.dart.strong.expect
new file mode 100644
index 0000000..a02b6ea
--- /dev/null
+++ b/pkg/front_end/testcases/dot_shorthands/simple_class.dart.strong.expect
@@ -0,0 +1,23 @@
+library;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/dot_shorthands/simple_class.dart:12:13: Error: Expected an identifier, but got '.'.
+// Try inserting an identifier before '.'.
+// Color c = .red;
+// ^
+//
+import self as self;
+import "dart:core" as core;
+
+class Color extends core::Object {
+ final field core::int x;
+ constructor •(core::int x) → self::Color
+ : self::Color::x = x, super core::Object::•()
+ ;
+ static get red() → self::Color
+ return new self::Color::•(1);
+}
+static method main() → void {
+ self::Color c = self::Color::red;
+}
diff --git a/pkg/front_end/testcases/dot_shorthands/simple_class.dart.strong.modular.expect b/pkg/front_end/testcases/dot_shorthands/simple_class.dart.strong.modular.expect
new file mode 100644
index 0000000..a02b6ea
--- /dev/null
+++ b/pkg/front_end/testcases/dot_shorthands/simple_class.dart.strong.modular.expect
@@ -0,0 +1,23 @@
+library;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/dot_shorthands/simple_class.dart:12:13: Error: Expected an identifier, but got '.'.
+// Try inserting an identifier before '.'.
+// Color c = .red;
+// ^
+//
+import self as self;
+import "dart:core" as core;
+
+class Color extends core::Object {
+ final field core::int x;
+ constructor •(core::int x) → self::Color
+ : self::Color::x = x, super core::Object::•()
+ ;
+ static get red() → self::Color
+ return new self::Color::•(1);
+}
+static method main() → void {
+ self::Color c = self::Color::red;
+}
diff --git a/pkg/front_end/testcases/dot_shorthands/simple_class.dart.strong.outline.expect b/pkg/front_end/testcases/dot_shorthands/simple_class.dart.strong.outline.expect
new file mode 100644
index 0000000..9d722eb
--- /dev/null
+++ b/pkg/front_end/testcases/dot_shorthands/simple_class.dart.strong.outline.expect
@@ -0,0 +1,13 @@
+library;
+import self as self;
+import "dart:core" as core;
+
+class Color extends core::Object {
+ final field core::int x;
+ constructor •(core::int x) → self::Color
+ ;
+ static get red() → self::Color
+ ;
+}
+static method main() → void
+ ;
diff --git a/pkg/front_end/testcases/dot_shorthands/simple_class.dart.strong.transformed.expect b/pkg/front_end/testcases/dot_shorthands/simple_class.dart.strong.transformed.expect
new file mode 100644
index 0000000..a02b6ea
--- /dev/null
+++ b/pkg/front_end/testcases/dot_shorthands/simple_class.dart.strong.transformed.expect
@@ -0,0 +1,23 @@
+library;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/dot_shorthands/simple_class.dart:12:13: Error: Expected an identifier, but got '.'.
+// Try inserting an identifier before '.'.
+// Color c = .red;
+// ^
+//
+import self as self;
+import "dart:core" as core;
+
+class Color extends core::Object {
+ final field core::int x;
+ constructor •(core::int x) → self::Color
+ : self::Color::x = x, super core::Object::•()
+ ;
+ static get red() → self::Color
+ return new self::Color::•(1);
+}
+static method main() → void {
+ self::Color c = self::Color::red;
+}
diff --git a/pkg/front_end/testcases/dot_shorthands/simple_class.dart.textual_outline.expect b/pkg/front_end/testcases/dot_shorthands/simple_class.dart.textual_outline.expect
new file mode 100644
index 0000000..c010157
--- /dev/null
+++ b/pkg/front_end/testcases/dot_shorthands/simple_class.dart.textual_outline.expect
@@ -0,0 +1,7 @@
+class Color {
+ final int x;
+ static Color get red => Color(1);
+ Color(this.x);
+}
+
+void main() {}
diff --git a/pkg/front_end/testcases/dot_shorthands/simple_class.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/dot_shorthands/simple_class.dart.textual_outline_modelled.expect
new file mode 100644
index 0000000..5e453b4
--- /dev/null
+++ b/pkg/front_end/testcases/dot_shorthands/simple_class.dart.textual_outline_modelled.expect
@@ -0,0 +1,7 @@
+class Color {
+ Color(this.x);
+ final int x;
+ static Color get red => Color(1);
+}
+
+void main() {}
diff --git a/pkg/front_end/testcases/dot_shorthands/simple_enum.dart b/pkg/front_end/testcases/dot_shorthands/simple_enum.dart
new file mode 100644
index 0000000..03dad1d
--- /dev/null
+++ b/pkg/front_end/testcases/dot_shorthands/simple_enum.dart
@@ -0,0 +1,9 @@
+// Copyright (c) 2025, 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 Color { red, blue, green }
+
+void main() {
+ Color c = .red;
+}
diff --git a/pkg/front_end/testcases/dot_shorthands/simple_enum.dart.strong.expect b/pkg/front_end/testcases/dot_shorthands/simple_enum.dart.strong.expect
new file mode 100644
index 0000000..072b755
--- /dev/null
+++ b/pkg/front_end/testcases/dot_shorthands/simple_enum.dart.strong.expect
@@ -0,0 +1,46 @@
+library;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/dot_shorthands/simple_enum.dart:8:13: Error: Expected an identifier, but got '.'.
+// Try inserting an identifier before '.'.
+// Color c = .red;
+// ^
+//
+import self as self;
+import "dart:core" as core;
+
+class Color extends core::_Enum /*isEnum*/ {
+ enum-element static const field self::Color red = #C3;
+ enum-element static const field self::Color blue = #C6;
+ enum-element static const field self::Color green = #C9;
+ static const field core::List<self::Color> values = #C10;
+ const synthetic constructor •(core::int #index, core::String #name) → self::Color
+ : super core::_Enum::•(#index, #name)
+ ;
+ method _enumToString() → core::String
+ return "Color.${this.{core::_Enum::_name}{core::String}}";
+}
+static method main() → void {
+ self::Color c = #C3;
+}
+
+constants {
+ #C1 = 0
+ #C2 = "red"
+ #C3 = self::Color {index:#C1, _name:#C2}
+ #C4 = 1
+ #C5 = "blue"
+ #C6 = self::Color {index:#C4, _name:#C5}
+ #C7 = 2
+ #C8 = "green"
+ #C9 = self::Color {index:#C7, _name:#C8}
+ #C10 = <self::Color>[#C3, #C6, #C9]
+}
+
+
+Constructor coverage from constants:
+org-dartlang-testcase:///simple_enum.dart:
+- Color. (from org-dartlang-testcase:///simple_enum.dart:5:6)
+- _Enum. (from org-dartlang-sdk:///sdk/lib/core/enum.dart)
+- Object. (from org-dartlang-sdk:///sdk/lib/core/object.dart)
diff --git a/pkg/front_end/testcases/dot_shorthands/simple_enum.dart.strong.modular.expect b/pkg/front_end/testcases/dot_shorthands/simple_enum.dart.strong.modular.expect
new file mode 100644
index 0000000..072b755
--- /dev/null
+++ b/pkg/front_end/testcases/dot_shorthands/simple_enum.dart.strong.modular.expect
@@ -0,0 +1,46 @@
+library;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/dot_shorthands/simple_enum.dart:8:13: Error: Expected an identifier, but got '.'.
+// Try inserting an identifier before '.'.
+// Color c = .red;
+// ^
+//
+import self as self;
+import "dart:core" as core;
+
+class Color extends core::_Enum /*isEnum*/ {
+ enum-element static const field self::Color red = #C3;
+ enum-element static const field self::Color blue = #C6;
+ enum-element static const field self::Color green = #C9;
+ static const field core::List<self::Color> values = #C10;
+ const synthetic constructor •(core::int #index, core::String #name) → self::Color
+ : super core::_Enum::•(#index, #name)
+ ;
+ method _enumToString() → core::String
+ return "Color.${this.{core::_Enum::_name}{core::String}}";
+}
+static method main() → void {
+ self::Color c = #C3;
+}
+
+constants {
+ #C1 = 0
+ #C2 = "red"
+ #C3 = self::Color {index:#C1, _name:#C2}
+ #C4 = 1
+ #C5 = "blue"
+ #C6 = self::Color {index:#C4, _name:#C5}
+ #C7 = 2
+ #C8 = "green"
+ #C9 = self::Color {index:#C7, _name:#C8}
+ #C10 = <self::Color>[#C3, #C6, #C9]
+}
+
+
+Constructor coverage from constants:
+org-dartlang-testcase:///simple_enum.dart:
+- Color. (from org-dartlang-testcase:///simple_enum.dart:5:6)
+- _Enum. (from org-dartlang-sdk:///sdk/lib/core/enum.dart)
+- Object. (from org-dartlang-sdk:///sdk/lib/core/object.dart)
diff --git a/pkg/front_end/testcases/dot_shorthands/simple_enum.dart.strong.outline.expect b/pkg/front_end/testcases/dot_shorthands/simple_enum.dart.strong.outline.expect
new file mode 100644
index 0000000..a370e0c
--- /dev/null
+++ b/pkg/front_end/testcases/dot_shorthands/simple_enum.dart.strong.outline.expect
@@ -0,0 +1,25 @@
+library;
+import self as self;
+import "dart:core" as core;
+
+class Color extends core::_Enum /*isEnum*/ {
+ enum-element static const field self::Color red = const self::Color::•(0, "red");
+ enum-element static const field self::Color blue = const self::Color::•(1, "blue");
+ enum-element static const field self::Color green = const self::Color::•(2, "green");
+ static const field core::List<self::Color> values = const <self::Color>[self::Color::red, self::Color::blue, self::Color::green];
+ const synthetic constructor •(core::int #index, core::String #name) → self::Color
+ : super core::_Enum::•(#index, #name)
+ ;
+ method _enumToString() → core::String
+ return "Color.${this.{core::_Enum::_name}{core::String}}";
+}
+static method main() → void
+ ;
+
+
+Extra constant evaluation status:
+Evaluated: ConstructorInvocation @ org-dartlang-testcase:///simple_enum.dart:5:14 -> InstanceConstant(const Color{_Enum.index: 0, _Enum._name: "red"})
+Evaluated: ConstructorInvocation @ org-dartlang-testcase:///simple_enum.dart:5:19 -> InstanceConstant(const Color{_Enum.index: 1, _Enum._name: "blue"})
+Evaluated: ConstructorInvocation @ org-dartlang-testcase:///simple_enum.dart:5:25 -> InstanceConstant(const Color{_Enum.index: 2, _Enum._name: "green"})
+Evaluated: ListLiteral @ org-dartlang-testcase:///simple_enum.dart:5:6 -> ListConstant(const <Color>[const Color{_Enum.index: 0, _Enum._name: "red"}, const Color{_Enum.index: 1, _Enum._name: "blue"}, const Color{_Enum.index: 2, _Enum._name: "green"}])
+Extra constant evaluation: evaluated: 9, effectively constant: 4
diff --git a/pkg/front_end/testcases/dot_shorthands/simple_enum.dart.strong.transformed.expect b/pkg/front_end/testcases/dot_shorthands/simple_enum.dart.strong.transformed.expect
new file mode 100644
index 0000000..072b755
--- /dev/null
+++ b/pkg/front_end/testcases/dot_shorthands/simple_enum.dart.strong.transformed.expect
@@ -0,0 +1,46 @@
+library;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/dot_shorthands/simple_enum.dart:8:13: Error: Expected an identifier, but got '.'.
+// Try inserting an identifier before '.'.
+// Color c = .red;
+// ^
+//
+import self as self;
+import "dart:core" as core;
+
+class Color extends core::_Enum /*isEnum*/ {
+ enum-element static const field self::Color red = #C3;
+ enum-element static const field self::Color blue = #C6;
+ enum-element static const field self::Color green = #C9;
+ static const field core::List<self::Color> values = #C10;
+ const synthetic constructor •(core::int #index, core::String #name) → self::Color
+ : super core::_Enum::•(#index, #name)
+ ;
+ method _enumToString() → core::String
+ return "Color.${this.{core::_Enum::_name}{core::String}}";
+}
+static method main() → void {
+ self::Color c = #C3;
+}
+
+constants {
+ #C1 = 0
+ #C2 = "red"
+ #C3 = self::Color {index:#C1, _name:#C2}
+ #C4 = 1
+ #C5 = "blue"
+ #C6 = self::Color {index:#C4, _name:#C5}
+ #C7 = 2
+ #C8 = "green"
+ #C9 = self::Color {index:#C7, _name:#C8}
+ #C10 = <self::Color>[#C3, #C6, #C9]
+}
+
+
+Constructor coverage from constants:
+org-dartlang-testcase:///simple_enum.dart:
+- Color. (from org-dartlang-testcase:///simple_enum.dart:5:6)
+- _Enum. (from org-dartlang-sdk:///sdk/lib/core/enum.dart)
+- Object. (from org-dartlang-sdk:///sdk/lib/core/object.dart)
diff --git a/pkg/front_end/testcases/dot_shorthands/simple_enum.dart.textual_outline.expect b/pkg/front_end/testcases/dot_shorthands/simple_enum.dart.textual_outline.expect
new file mode 100644
index 0000000..3931b1c
--- /dev/null
+++ b/pkg/front_end/testcases/dot_shorthands/simple_enum.dart.textual_outline.expect
@@ -0,0 +1,3 @@
+enum Color { red, blue, green }
+
+void main() {}
diff --git a/pkg/front_end/testcases/dot_shorthands/simple_enum.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/dot_shorthands/simple_enum.dart.textual_outline_modelled.expect
new file mode 100644
index 0000000..3931b1c
--- /dev/null
+++ b/pkg/front_end/testcases/dot_shorthands/simple_enum.dart.textual_outline_modelled.expect
@@ -0,0 +1,3 @@
+enum Color { red, blue, green }
+
+void main() {}
diff --git a/pkg/front_end/testcases/dot_shorthands/tearoff.dart b/pkg/front_end/testcases/dot_shorthands/tearoff.dart
new file mode 100644
index 0000000..e9986ca
--- /dev/null
+++ b/pkg/front_end/testcases/dot_shorthands/tearoff.dart
@@ -0,0 +1,7 @@
+// Copyright (c) 2025, 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.
+
+void main() {
+ Object o = .hash;
+}
diff --git a/pkg/front_end/testcases/dot_shorthands/tearoff.dart.strong.expect b/pkg/front_end/testcases/dot_shorthands/tearoff.dart.strong.expect
new file mode 100644
index 0000000..0849169
--- /dev/null
+++ b/pkg/front_end/testcases/dot_shorthands/tearoff.dart.strong.expect
@@ -0,0 +1,19 @@
+library;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/dot_shorthands/tearoff.dart:6:14: Error: Expected an identifier, but got '.'.
+// Try inserting an identifier before '.'.
+// Object o = .hash;
+// ^
+//
+import self as self;
+import "dart:core" as core;
+
+static method main() → void {
+ core::Object o = #C1;
+}
+
+constants {
+ #C1 = static-tearoff core::Object::hash
+}
diff --git a/pkg/front_end/testcases/dot_shorthands/tearoff.dart.strong.modular.expect b/pkg/front_end/testcases/dot_shorthands/tearoff.dart.strong.modular.expect
new file mode 100644
index 0000000..0849169
--- /dev/null
+++ b/pkg/front_end/testcases/dot_shorthands/tearoff.dart.strong.modular.expect
@@ -0,0 +1,19 @@
+library;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/dot_shorthands/tearoff.dart:6:14: Error: Expected an identifier, but got '.'.
+// Try inserting an identifier before '.'.
+// Object o = .hash;
+// ^
+//
+import self as self;
+import "dart:core" as core;
+
+static method main() → void {
+ core::Object o = #C1;
+}
+
+constants {
+ #C1 = static-tearoff core::Object::hash
+}
diff --git a/pkg/front_end/testcases/dot_shorthands/tearoff.dart.strong.outline.expect b/pkg/front_end/testcases/dot_shorthands/tearoff.dart.strong.outline.expect
new file mode 100644
index 0000000..2982c48
--- /dev/null
+++ b/pkg/front_end/testcases/dot_shorthands/tearoff.dart.strong.outline.expect
@@ -0,0 +1,5 @@
+library;
+import self as self;
+
+static method main() → void
+ ;
diff --git a/pkg/front_end/testcases/dot_shorthands/tearoff.dart.strong.transformed.expect b/pkg/front_end/testcases/dot_shorthands/tearoff.dart.strong.transformed.expect
new file mode 100644
index 0000000..0849169
--- /dev/null
+++ b/pkg/front_end/testcases/dot_shorthands/tearoff.dart.strong.transformed.expect
@@ -0,0 +1,19 @@
+library;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/dot_shorthands/tearoff.dart:6:14: Error: Expected an identifier, but got '.'.
+// Try inserting an identifier before '.'.
+// Object o = .hash;
+// ^
+//
+import self as self;
+import "dart:core" as core;
+
+static method main() → void {
+ core::Object o = #C1;
+}
+
+constants {
+ #C1 = static-tearoff core::Object::hash
+}
diff --git a/pkg/front_end/testcases/dot_shorthands/tearoff.dart.textual_outline.expect b/pkg/front_end/testcases/dot_shorthands/tearoff.dart.textual_outline.expect
new file mode 100644
index 0000000..ab73b3a
--- /dev/null
+++ b/pkg/front_end/testcases/dot_shorthands/tearoff.dart.textual_outline.expect
@@ -0,0 +1 @@
+void main() {}
diff --git a/pkg/front_end/testcases/dot_shorthands/tearoff.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/dot_shorthands/tearoff.dart.textual_outline_modelled.expect
new file mode 100644
index 0000000..ab73b3a
--- /dev/null
+++ b/pkg/front_end/testcases/dot_shorthands/tearoff.dart.textual_outline_modelled.expect
@@ -0,0 +1 @@
+void main() {}