[cfe] Handle extension type declarations with dot shorthands.
This CL adds the work needed to be able to use dot shorthands with extension types.
Also, there's some additional work to set `isSetter` to false for dot shorthands. Static setters don't work together with shorthands.
Bug: https://github.com/dart-lang/sdk/issues/59758
Change-Id: I2c14606cf2970ee249f48189c686ea5cb963df0b
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/412784
Reviewed-by: Chloe Stefantsova <cstefantsova@google.com>
Commit-Queue: Kallen Tu <kallentu@google.com>
diff --git a/pkg/front_end/lib/src/kernel/hierarchy/extension_type_members.dart b/pkg/front_end/lib/src/kernel/hierarchy/extension_type_members.dart
index 0805f89..8cea3e4 100644
--- a/pkg/front_end/lib/src/kernel/hierarchy/extension_type_members.dart
+++ b/pkg/front_end/lib/src/kernel/hierarchy/extension_type_members.dart
@@ -279,6 +279,18 @@
}
return result;
}
+
+ ClassMember? getStaticMember(Name name, bool isSetter) {
+ ClassMember? result = isSetter
+ ? (extensionTypeSetableMap?[name] ?? nonExtensionTypeSetableMap?[name])
+ : (extensionTypeGetableMap?[name] ?? nonExtensionTypeGetableMap?[name]);
+ if (result == null) {
+ return null;
+ } else if (result.isStatic) {
+ return result;
+ }
+ return null;
+ }
}
class _Tuple {
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 18a8363..44216a7 100644
--- a/pkg/front_end/lib/src/kernel/hierarchy/members_builder.dart
+++ b/pkg/front_end/lib/src/kernel/hierarchy/members_builder.dart
@@ -190,6 +190,13 @@
.getMember(name, setter);
}
+ ClassMember? getExtensionTypeStaticClassMember(
+ ExtensionTypeDeclaration extensionTypeDeclaration, Name name,
+ {bool setter = false}) {
+ return getNodeFromExtensionTypeDeclaration(extensionTypeDeclaration)
+ .getStaticMember(name, setter);
+ }
+
@override
Member? getDispatchTarget(Class cls, Name name, {bool setter = false}) {
return getNodeFromClass(cls)
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 a240bff..8fafd5d 100644
--- a/pkg/front_end/lib/src/kernel/hierarchy/members_node.dart
+++ b/pkg/front_end/lib/src/kernel/hierarchy/members_node.dart
@@ -1189,7 +1189,8 @@
}
ClassMember? getStaticMember(Name name, bool isSetter) {
- ClassMember? result = classMemberMap[name];
+ ClassMember? result =
+ isSetter ? classSetterMap[name] : classMemberMap[name];
if (result == null) {
return null;
} else if (result.isStatic) {
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 ab40cdf..93eed0e 100644
--- a/pkg/front_end/lib/src/type_inference/inference_visitor.dart
+++ b/pkg/front_end/lib/src/type_inference/inference_visitor.dart
@@ -12120,9 +12120,7 @@
DartType cachedContext = getDotShorthandContext().unwrapTypeSchemaView();
Member? member = findInterfaceMember(
cachedContext, node.name, node.fileOffset,
- includeExtensionMethods: false,
- isSetter: false,
- isDotShorthand: true)
+ isSetter: false, isDotShorthand: true)
.member;
ExpressionInferenceResult 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 200357e..832ff59 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
@@ -968,9 +968,14 @@
ObjectAccessTarget? _findExtensionTypeMember(DartType receiverType,
ExtensionType extensionType, Name name, int fileOffset,
- {required bool isSetter, required bool hasNonObjectMemberAccess}) {
- ClassMember? classMember = _getExtensionTypeMember(
- extensionType.extensionTypeDeclaration, name, isSetter);
+ {required bool isSetter,
+ required bool hasNonObjectMemberAccess,
+ bool isDotShorthand = false}) {
+ ClassMember? classMember = isDotShorthand
+ ? _getExtensionTypeStaticMember(
+ extensionType.extensionTypeDeclaration, name, false)
+ : _getExtensionTypeMember(
+ extensionType.extensionTypeDeclaration, name, isSetter);
if (classMember == null) {
return null;
}
@@ -3752,6 +3757,18 @@
return member;
}
+ ClassMember? _getExtensionTypeStaticMember(
+ ExtensionTypeDeclaration extensionTypeDeclaration,
+ Name name,
+ bool setter) {
+ ClassMember? member = engine.membersBuilder
+ .getExtensionTypeStaticClassMember(extensionTypeDeclaration, name,
+ setter: setter);
+ TypeInferenceEngine.resolveInferenceNode(
+ member?.getMember(engine.membersBuilder), hierarchyBuilder);
+ return member;
+ }
+
bool _isLoweredSetLiteral(Expression expression) {
if (libraryBuilder.loader.target.backendTarget.supportsSetLiterals) {
return false;
@@ -4720,7 +4737,8 @@
ObjectAccessTarget? target = visitor._findExtensionTypeMember(
receiverType, receiverBound, name, fileOffset,
isSetter: isSetter,
- hasNonObjectMemberAccess: hasNonObjectMemberAccess);
+ hasNonObjectMemberAccess: hasNonObjectMemberAccess,
+ isDotShorthand: isDotShorthand);
if (target != null) {
return target;
}
@@ -4728,7 +4746,7 @@
ObjectAccessTarget? target;
Member? interfaceMember = isDotShorthand
- ? visitor._getStaticMember(classNode, name, isSetter)
+ ? visitor._getStaticMember(classNode, name, false)
: visitor._getInterfaceMember(classNode, name, isSetter);
if (interfaceMember != null) {
target = new ObjectAccessTarget.interfaceMember(
diff --git a/pkg/front_end/testcases/dot_shorthands/extension_type.dart b/pkg/front_end/testcases/dot_shorthands/extension_type.dart
new file mode 100644
index 0000000..44f92a1
--- /dev/null
+++ b/pkg/front_end/testcases/dot_shorthands/extension_type.dart
@@ -0,0 +1,11 @@
+// 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.
+
+extension type IntegerExt(int integer) {
+ static IntegerExt get one => IntegerExt(1);
+}
+
+void main() {
+ IntegerExt c = .one;
+}
diff --git a/pkg/front_end/testcases/dot_shorthands/extension_type.dart.strong.expect b/pkg/front_end/testcases/dot_shorthands/extension_type.dart.strong.expect
new file mode 100644
index 0000000..735d884
--- /dev/null
+++ b/pkg/front_end/testcases/dot_shorthands/extension_type.dart.strong.expect
@@ -0,0 +1,21 @@
+library;
+import self as self;
+import "dart:core" as core;
+
+extension type IntegerExt(core::int integer) {
+ abstract extension-type-member representation-field get integer() → core::int;
+ static get one = get self::IntegerExt|one;
+ constructor • = self::IntegerExt|constructor#;
+ constructor tearoff • = self::IntegerExt|constructor#_#new#tearOff;
+}
+static extension-type-member method IntegerExt|constructor#(core::int integer) → self::IntegerExt% /* erasure=core::int, declared=! */ {
+ lowered final self::IntegerExt% /* erasure=core::int, declared=! */ #this = integer;
+ return #this;
+}
+static extension-type-member method IntegerExt|constructor#_#new#tearOff(core::int integer) → self::IntegerExt% /* erasure=core::int, declared=! */
+ return self::IntegerExt|constructor#(integer);
+static extension-type-member get IntegerExt|one() → self::IntegerExt% /* erasure=core::int, declared=! */
+ return self::IntegerExt|constructor#(1);
+static method main() → void {
+ self::IntegerExt% /* erasure=core::int, declared=! */ c = self::IntegerExt|one;
+}
diff --git a/pkg/front_end/testcases/dot_shorthands/extension_type.dart.strong.modular.expect b/pkg/front_end/testcases/dot_shorthands/extension_type.dart.strong.modular.expect
new file mode 100644
index 0000000..735d884
--- /dev/null
+++ b/pkg/front_end/testcases/dot_shorthands/extension_type.dart.strong.modular.expect
@@ -0,0 +1,21 @@
+library;
+import self as self;
+import "dart:core" as core;
+
+extension type IntegerExt(core::int integer) {
+ abstract extension-type-member representation-field get integer() → core::int;
+ static get one = get self::IntegerExt|one;
+ constructor • = self::IntegerExt|constructor#;
+ constructor tearoff • = self::IntegerExt|constructor#_#new#tearOff;
+}
+static extension-type-member method IntegerExt|constructor#(core::int integer) → self::IntegerExt% /* erasure=core::int, declared=! */ {
+ lowered final self::IntegerExt% /* erasure=core::int, declared=! */ #this = integer;
+ return #this;
+}
+static extension-type-member method IntegerExt|constructor#_#new#tearOff(core::int integer) → self::IntegerExt% /* erasure=core::int, declared=! */
+ return self::IntegerExt|constructor#(integer);
+static extension-type-member get IntegerExt|one() → self::IntegerExt% /* erasure=core::int, declared=! */
+ return self::IntegerExt|constructor#(1);
+static method main() → void {
+ self::IntegerExt% /* erasure=core::int, declared=! */ c = self::IntegerExt|one;
+}
diff --git a/pkg/front_end/testcases/dot_shorthands/extension_type.dart.strong.outline.expect b/pkg/front_end/testcases/dot_shorthands/extension_type.dart.strong.outline.expect
new file mode 100644
index 0000000..87d5e37
--- /dev/null
+++ b/pkg/front_end/testcases/dot_shorthands/extension_type.dart.strong.outline.expect
@@ -0,0 +1,18 @@
+library;
+import self as self;
+import "dart:core" as core;
+
+extension type IntegerExt(core::int integer) {
+ abstract extension-type-member representation-field get integer() → core::int;
+ static get one = get self::IntegerExt|one;
+ constructor • = self::IntegerExt|constructor#;
+ constructor tearoff • = self::IntegerExt|constructor#_#new#tearOff;
+}
+static extension-type-member method IntegerExt|constructor#(core::int integer) → self::IntegerExt% /* erasure=core::int, declared=! */
+ ;
+static extension-type-member method IntegerExt|constructor#_#new#tearOff(core::int integer) → self::IntegerExt% /* erasure=core::int, declared=! */
+ return self::IntegerExt|constructor#(integer);
+static extension-type-member get IntegerExt|one() → self::IntegerExt% /* erasure=core::int, declared=! */
+ ;
+static method main() → void
+ ;
diff --git a/pkg/front_end/testcases/dot_shorthands/extension_type.dart.strong.transformed.expect b/pkg/front_end/testcases/dot_shorthands/extension_type.dart.strong.transformed.expect
new file mode 100644
index 0000000..735d884
--- /dev/null
+++ b/pkg/front_end/testcases/dot_shorthands/extension_type.dart.strong.transformed.expect
@@ -0,0 +1,21 @@
+library;
+import self as self;
+import "dart:core" as core;
+
+extension type IntegerExt(core::int integer) {
+ abstract extension-type-member representation-field get integer() → core::int;
+ static get one = get self::IntegerExt|one;
+ constructor • = self::IntegerExt|constructor#;
+ constructor tearoff • = self::IntegerExt|constructor#_#new#tearOff;
+}
+static extension-type-member method IntegerExt|constructor#(core::int integer) → self::IntegerExt% /* erasure=core::int, declared=! */ {
+ lowered final self::IntegerExt% /* erasure=core::int, declared=! */ #this = integer;
+ return #this;
+}
+static extension-type-member method IntegerExt|constructor#_#new#tearOff(core::int integer) → self::IntegerExt% /* erasure=core::int, declared=! */
+ return self::IntegerExt|constructor#(integer);
+static extension-type-member get IntegerExt|one() → self::IntegerExt% /* erasure=core::int, declared=! */
+ return self::IntegerExt|constructor#(1);
+static method main() → void {
+ self::IntegerExt% /* erasure=core::int, declared=! */ c = self::IntegerExt|one;
+}
diff --git a/pkg/front_end/testcases/dot_shorthands/extension_type.dart.textual_outline.expect b/pkg/front_end/testcases/dot_shorthands/extension_type.dart.textual_outline.expect
new file mode 100644
index 0000000..85d1476
--- /dev/null
+++ b/pkg/front_end/testcases/dot_shorthands/extension_type.dart.textual_outline.expect
@@ -0,0 +1,5 @@
+extension type IntegerExt(int integer) {
+ static IntegerExt get one => IntegerExt(1);
+}
+
+void main() {}
diff --git a/pkg/front_end/testcases/dot_shorthands/extension_type.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/dot_shorthands/extension_type.dart.textual_outline_modelled.expect
new file mode 100644
index 0000000..85d1476
--- /dev/null
+++ b/pkg/front_end/testcases/dot_shorthands/extension_type.dart.textual_outline_modelled.expect
@@ -0,0 +1,5 @@
+extension type IntegerExt(int integer) {
+ static IntegerExt get one => IntegerExt(1);
+}
+
+void main() {}