[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() {}