[cfe] Improve predicate helper for extension (type) members

This adds support for computing the qualified name for all extension and extension type members.

The `lowering_predicates_test.dart` is removed and testing is performed through `predicate_test.dart`.

Change-Id: I334bb85d48b6b1fd8fa01de6576f61168b3e96fa
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/424443
Reviewed-by: Jens Johansen <jensj@google.com>
Commit-Queue: Johnni Winther <johnniwinther@google.com>
diff --git a/pkg/front_end/lib/src/api_prototype/lowering_predicates.dart b/pkg/front_end/lib/src/api_prototype/lowering_predicates.dart
index b071971..3973221 100644
--- a/pkg/front_end/lib/src/api_prototype/lowering_predicates.dart
+++ b/pkg/front_end/lib/src/api_prototype/lowering_predicates.dart
@@ -5,6 +5,7 @@
 import 'package:kernel/ast.dart';
 
 import '../kernel/late_lowering.dart';
+import '../source/name_scheme.dart';
 
 export '../kernel/constructor_tearoff_lowering.dart'
     show
@@ -845,10 +846,78 @@
   return '$variableName$joinedIntermediateInfix$index';
 }
 
-/// This turns Foo|bar into Foo.bar.
+const String unnamedExtensionSentinel = '<unnamed extension>';
+
+/// Returns the qualified name of an extension or extension type member.
 ///
-/// This only works for normal methods and operators, but for getters and
-/// setters.
+/// This turns `Foo|bar` and `Foo|get#bar` into `Foo.bar`.
+///
+/// For members of unnamed extensions, the extension name
+/// [unnamedExtensionSentinel] is used.
+///
+/// No-name extension type constructors use `new` as the name.
+///
+/// DartDocTest(
+///   extractQualifiedNameFromExtensionMethodName(null),
+///   null)
+/// DartDocTest(
+///   extractQualifiedNameFromExtensionMethodName('Foo'),
+///   null)
+/// DartDocTest(
+///   extractQualifiedNameFromExtensionMethodName('Foo|bar'),
+///   'Foo.bar')
+/// DartDocTest(
+///   extractQualifiedNameFromExtensionMethodName('Foo|get#bar'),
+///   'Foo.bar')
+/// DartDocTest(
+///   extractQualifiedNameFromExtensionMethodName('Foo|set#bar'),
+///   'Foo.bar')
+/// DartDocTest(
+///   extractQualifiedNameFromExtensionMethodName('Foo|constructor#bar'),
+///   'Foo.bar')
+/// DartDocTest(
+///   extractQualifiedNameFromExtensionMethodName('_extension#1|bar'),
+///   '<unnamed extension>.bar')
+/// DartDocTest(
+///   extractQualifiedNameFromExtensionMethodName('_extension#2|get#bar'),
+///   '<unnamed extension>.bar')
+/// DartDocTest(
+///   extractQualifiedNameFromExtensionMethodName('_extension#3|set#bar'),
+///   '<unnamed extension>.bar')
+///
 String? extractQualifiedNameFromExtensionMethodName(String? methodName) {
-  return methodName?.replaceFirst('|', '.');
+  if (methodName == null) return null;
+  int delimiterIndex = methodName.indexOf(NameScheme.extensionNameDelimiter);
+  if (delimiterIndex == -1) return null;
+  String extensionName = methodName.substring(0, delimiterIndex);
+  if (extensionName.startsWith(NameScheme.unnamedExtensionNamePrefix)) {
+    extensionName = unnamedExtensionSentinel;
+  }
+  String memberName = methodName
+      .substring(delimiterIndex + NameScheme.extensionNameDelimiter.length);
+  if (memberName.startsWith(NameScheme.extensionTypeConstructorPrefix)) {
+    memberName =
+        memberName.substring(NameScheme.extensionTypeConstructorPrefix.length);
+  }
+  if (memberName.startsWith(NameScheme.extensionGetterPrefix)) {
+    memberName = memberName.substring(NameScheme.extensionGetterPrefix.length);
+  }
+  if (memberName.startsWith(NameScheme.extensionSetterPrefix)) {
+    // Coverage-ignore-block(suite): Not run.
+    memberName = memberName.substring(NameScheme.extensionSetterPrefix.length);
+  }
+  if (memberName.isEmpty) {
+    memberName = 'new';
+  }
+  return '$extensionName.$memberName';
+}
+
+// Coverage-ignore(suite): Not run.
+/// Returns the qualified name of [member] if it is an extension or extension
+/// type member, and otherwise `null`.
+String? extractQualifiedNameFromExtensionMember(Member member) {
+  if (member.isExtensionMember || member.isExtensionTypeMember) {
+    return extractQualifiedNameFromExtensionMethodName(member.name.text);
+  }
+  return null;
 }
diff --git a/pkg/front_end/lib/src/base/incremental_compiler.dart b/pkg/front_end/lib/src/base/incremental_compiler.dart
index fe2298a..29b0531 100644
--- a/pkg/front_end/lib/src/base/incremental_compiler.dart
+++ b/pkg/front_end/lib/src/base/incremental_compiler.dart
@@ -2652,6 +2652,7 @@
     // Report old problems that wasn't reported again.
     for (MapEntry<Uri, List<DiagnosticMessageFromJson>> entry
         in _remainingComponentProblems.entries) {
+      // Coverage-ignore-block(suite): Not run.
       List<DiagnosticMessageFromJson> messages = entry.value;
       for (int i = 0; i < messages.length; i++) {
         DiagnosticMessageFromJson message = messages[i];
diff --git a/pkg/front_end/lib/src/codes/type_labeler.dart b/pkg/front_end/lib/src/codes/type_labeler.dart
index f0204cb..0635695 100644
--- a/pkg/front_end/lib/src/codes/type_labeler.dart
+++ b/pkg/front_end/lib/src/codes/type_labeler.dart
@@ -370,7 +370,9 @@
     result.add(">[");
     bool first = true;
     for (Constant constant in node.entries) {
-      if (!first) result.add(", ");
+      if (!first) {
+        result.add(", ");
+      }
       constant.accept(this);
       first = false;
     }
diff --git a/pkg/front_end/lib/src/source/name_scheme.dart b/pkg/front_end/lib/src/source/name_scheme.dart
index 43102e1..39f1cba 100644
--- a/pkg/front_end/lib/src/source/name_scheme.dart
+++ b/pkg/front_end/lib/src/source/name_scheme.dart
@@ -124,20 +124,11 @@
     }
   }
 
-  // Coverage-ignore(suite): Not run.
-  static String createProcedureNameForTesting(
-      {required ContainerName? containerName,
-      required ContainerType containerType,
-      required bool isStatic,
-      required ProcedureKind kind,
-      required String name}) {
-    return _createProcedureName(
-        containerName: containerName,
-        containerType: containerType,
-        isStatic: isStatic,
-        kind: kind,
-        name: name);
-  }
+  static const String extensionTypeConstructorPrefix = 'constructor#';
+  static const String extensionGetterPrefix = 'get#';
+  static const String extensionSetterPrefix = 'set#';
+  static const String extensionNameDelimiter = '|';
+  static const String unnamedExtensionNamePrefix = '_extension#';
 
   static String _createProcedureName(
       {required ContainerName? containerName,
@@ -155,10 +146,10 @@
           // infix to make their names unique.
           switch (kind) {
             case ProcedureKind.Getter:
-              kindInfix = 'get#';
+              kindInfix = extensionGetterPrefix;
               break;
             case ProcedureKind.Setter:
-              kindInfix = 'set#';
+              kindInfix = extensionSetterPrefix;
               break;
             case ProcedureKind.Method:
             case ProcedureKind.Operator:
@@ -170,7 +161,7 @@
                   'Unexpected extension method kind ${kind}');
           }
         }
-        return '${extensionName}|${kindInfix}${name}';
+        return '${extensionName}${extensionNameDelimiter}${kindInfix}${name}';
       // Coverage-ignore(suite): Not run.
       case ContainerType.Library:
       case ContainerType.Class:
@@ -512,7 +503,11 @@
       name = _text;
     }
     return new Name.byReference(
-        '${className}|constructor#${name}', _libraryName.reference);
+        '${className}'
+        '${NameScheme.extensionNameDelimiter}'
+        '${NameScheme.extensionTypeConstructorPrefix}'
+        '${name}',
+        _libraryName.reference);
   }
 }
 
diff --git a/pkg/front_end/lib/src/source/source_library_builder.dart b/pkg/front_end/lib/src/source/source_library_builder.dart
index 4347ef3..41d3c08 100644
--- a/pkg/front_end/lib/src/source/source_library_builder.dart
+++ b/pkg/front_end/lib/src/source/source_library_builder.dart
@@ -913,7 +913,8 @@
       if (!declaration.isDuplicate) {
         if (declaration.isUnnamedExtension) {
           declaration.extensionName.name =
-              '_extension#${library.extensions.length}';
+              '${NameScheme.unnamedExtensionNamePrefix}'
+              '${library.extensions.length}';
         }
         library.addExtension(extension);
       }
diff --git a/pkg/front_end/lib/src/type_inference/object_access_target.dart b/pkg/front_end/lib/src/type_inference/object_access_target.dart
index 28222e4..787a160 100644
--- a/pkg/front_end/lib/src/type_inference/object_access_target.dart
+++ b/pkg/front_end/lib/src/type_inference/object_access_target.dart
@@ -899,19 +899,22 @@
 class DynamicAccessTarget extends ObjectAccessTarget {
   /// Creates an access on a dynamic receiver type with no known target.
   const DynamicAccessTarget.dynamic()
-      : super.internal(ObjectAccessTargetKind.dynamic);
+      : super // Coverage-ignore(suite): Not run.
+        .internal(ObjectAccessTargetKind.dynamic);
 
   /// Creates an access with no target due to an invalid receiver type.
   ///
   /// This is not in itself an error but a consequence of another error.
   const DynamicAccessTarget.invalid()
-      : super.internal(ObjectAccessTargetKind.invalid);
+      : super // Coverage-ignore(suite): Not run.
+        .internal(ObjectAccessTargetKind.invalid);
 
   /// Creates an access with no target.
   ///
   /// This is an error case.
   const DynamicAccessTarget.missing()
-      : super.internal(ObjectAccessTargetKind.missing);
+      : super // Coverage-ignore(suite): Not run.
+        .internal(ObjectAccessTargetKind.missing);
 
   @override
   // Coverage-ignore(suite): Not run.
diff --git a/pkg/front_end/test/coverage_suite_expected.dart b/pkg/front_end/test/coverage_suite_expected.dart
index 012277a..a0b2243 100644
--- a/pkg/front_end/test/coverage_suite_expected.dart
+++ b/pkg/front_end/test/coverage_suite_expected.dart
@@ -45,7 +45,7 @@
   ),
   // 100.0%.
   "package:front_end/src/api_prototype/lowering_predicates.dart": (
-    hitCount: 15,
+    hitCount: 31,
     missCount: 0,
   ),
   // 100.0%.
@@ -150,7 +150,7 @@
   ),
   // 100.0%.
   "package:front_end/src/base/incremental_compiler.dart": (
-    hitCount: 829,
+    hitCount: 819,
     missCount: 0,
   ),
   // 100.0%.
@@ -1076,7 +1076,7 @@
   ),
   // 100.0%.
   "package:front_end/src/type_inference/object_access_target.dart": (
-    hitCount: 552,
+    hitCount: 549,
     missCount: 0,
   ),
   // 100.0%.
diff --git a/pkg/front_end/test/lowering_predicates_test.dart b/pkg/front_end/test/lowering_predicates_test.dart
deleted file mode 100644
index 9cea1fe..0000000
--- a/pkg/front_end/test/lowering_predicates_test.dart
+++ /dev/null
@@ -1,53 +0,0 @@
-// Copyright (c) 2023, 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.
-
-import 'package:front_end/src/api_prototype/lowering_predicates.dart';
-import 'package:front_end/src/source/name_scheme.dart';
-import 'package:kernel/ast.dart';
-
-void main() {
-  testExtractQualifiedNameFromExtensionMethodName();
-}
-
-void testExtractQualifiedNameFromExtensionMethodName() {
-  // Doesn't crash on null and returns null in that case.
-  expect(extractQualifiedNameFromExtensionMethodName(null), null);
-
-  // When given data it actually extracts what we want.
-  for (ContainerType containerType in [
-    ContainerType.ExtensionType,
-    ContainerType.Extension
-  ]) {
-    for (bool isStatic in [true, false]) {
-      String encodedName = NameScheme.createProcedureNameForTesting(
-          containerName: new TesterContainerName("Foo"),
-          containerType: containerType,
-          isStatic: isStatic,
-          kind: ProcedureKind.Method,
-          name: "bar");
-      String extracted =
-          extractQualifiedNameFromExtensionMethodName(encodedName)!;
-      expectDifferent(encodedName, extracted);
-      expect(extracted, "Foo.bar");
-    }
-  }
-}
-
-class TesterContainerName extends ContainerName {
-  @override
-  final String name;
-
-  TesterContainerName(this.name);
-
-  @override
-  void attachMemberName(MemberName name) {}
-}
-
-void expect(Object? actual, Object? expect) {
-  if (expect != actual) throw "Expected $expect got $actual";
-}
-
-void expectDifferent(Object? actual, Object? expectNot) {
-  if (expectNot == actual) throw "Expected not $expectNot got $actual";
-}
diff --git a/pkg/front_end/test/predicates/data/extension.dart b/pkg/front_end/test/predicates/data/extension.dart
index 2b84ac8..03aac6e 100644
--- a/pkg/front_end/test/predicates/data/extension.dart
+++ b/pkg/front_end/test/predicates/data/extension.dart
@@ -3,27 +3,67 @@
 // BSD-style license that can be found in the LICENSE file.
 
 extension Extension on int {
+  /*member: Extension|instanceMethod:extensionName=Extension.instanceMethod*/
+  /*member: Extension|get#instanceMethod:extensionName=Extension.instanceMethod*/
   int /*
    extensionThis,
    name=this
   */
       instanceMethod() => this;
 
+  /*member: Extension|get#instanceGetter:extensionName=Extension.instanceGetter*/
   int get /*
    extensionThis,
    name=this
   */
       instanceGetter => this;
 
+  /*member: Extension|set#instanceSetter:extensionName=Extension.instanceSetter*/
   void set /*
    extensionThis,
    name=this
   */
       instanceSetter(int value) {}
 
+  /*member: Extension|staticMethod:extensionName=Extension.staticMethod*/
   static int staticMethod() => 42;
 
+  /*member: Extension|staticGetter:extensionName=Extension.staticGetter*/
   static int get staticGetter => 42;
 
+  /*member: Extension|staticSetter=:extensionName=Extension.staticSetter*/
+  static void set staticSetter(int value) {}
+}
+
+extension on int {
+  /*member: _extension#1|instanceMethod:extensionName=<unnamed extension>.instanceMethod*/
+  /*member: _extension#1|get#instanceMethod:extensionName=<unnamed extension>.instanceMethod*/
+  int /*
+   extensionThis,
+   name=this
+  */
+      instanceMethod() => this;
+
+  /*member: _extension#1|get#instanceGetter:extensionName=<unnamed extension>.instanceGetter*/
+  int get /*
+   extensionThis,
+   name=this
+  */
+      instanceGetter => this;
+
+  /*member: _extension#1|set#instanceSetter:extensionName=<unnamed extension>.instanceSetter*/
+  void set /*
+   extensionThis,
+   name=this
+  */
+      instanceSetter(int value) {}
+
+  /*member: _extension#1|staticMethod:extensionName=<unnamed extension>.staticMethod*/
+  static int staticMethod() => 42;
+
+  /*member: _extension#1|staticGetter:extensionName=<unnamed extension>.staticGetter*/
+  static int get staticGetter => 42;
+
+  /*member: _extension#1|staticSetter=:extensionName=<unnamed extension>.staticSetter*/
   static void set staticSetter(int value) {}
 }
diff --git a/pkg/front_end/test/predicates/data/extension_type.dart b/pkg/front_end/test/predicates/data/extension_type.dart
new file mode 100644
index 0000000..6bffeca
--- /dev/null
+++ b/pkg/front_end/test/predicates/data/extension_type.dart
@@ -0,0 +1,45 @@
+// 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.
+
+/*member: ExtensionType|constructor#:extensionName=ExtensionType.new*/
+extension type ExtensionType(int it) {
+  /*member: ExtensionType|constructor#constructor:extensionName=ExtensionType.constructor*/
+  ExtensionType.constructor(this.it);
+
+  /*member: ExtensionType|constructor#factory:extensionName=ExtensionType.factory*/
+  factory ExtensionType.factory(int value) => ExtensionType(value);
+
+  /*member: ExtensionType|constructor#redirectingFactory:extensionName=ExtensionType.redirectingFactory*/
+  factory ExtensionType.redirectingFactory(int value) = ExtensionType;
+
+  /*member: ExtensionType|instanceMethod:extensionName=ExtensionType.instanceMethod*/
+  int /*
+   extensionThis,
+   name=this
+  */
+      instanceMethod() => this.it;
+
+  /*member: ExtensionType|get#instanceGetter:extensionName=ExtensionType.instanceGetter*/
+  int get /*
+   extensionThis,
+   name=this
+  */
+      instanceGetter => this.it;
+
+  /*member: ExtensionType|set#instanceSetter:extensionName=ExtensionType.instanceSetter*/
+  void set /*
+   extensionThis,
+   name=this
+  */
+      instanceSetter(int value) {}
+
+  /*member: ExtensionType|staticMethod:extensionName=ExtensionType.staticMethod*/
+  static int staticMethod() => 42;
+
+  /*member: ExtensionType|staticGetter:extensionName=ExtensionType.staticGetter*/
+  static int get staticGetter => 42;
+
+  /*member: ExtensionType|staticSetter=:extensionName=ExtensionType.staticSetter*/
+  static void set staticSetter(int value) {}
+}
diff --git a/pkg/front_end/test/predicates/data/late.dart b/pkg/front_end/test/predicates/data/late.dart
index ae84ae2..2182264 100644
--- a/pkg/front_end/test/predicates/data/late.dart
+++ b/pkg/front_end/test/predicates/data/late.dart
@@ -468,11 +468,13 @@
    lateFieldTarget=_#_extension#1|unnamedExtensionNonNullableWithoutInitializer
   */
   /*member: _extension#1|unnamedExtensionNonNullableWithoutInitializer:
+   extensionName=<unnamed extension>.unnamedExtensionNonNullableWithoutInitializer,
    lateFieldGetter,
    lateFieldName=_extension#1|unnamedExtensionNonNullableWithoutInitializer,
    lateFieldTarget=_#_extension#1|unnamedExtensionNonNullableWithoutInitializer
   */
   /*member: _extension#1|unnamedExtensionNonNullableWithoutInitializer=:
+   extensionName=<unnamed extension>.unnamedExtensionNonNullableWithoutInitializer,
    lateFieldName=_extension#1|unnamedExtensionNonNullableWithoutInitializer,
    lateFieldSetter,
    lateFieldTarget=_#_extension#1|unnamedExtensionNonNullableWithoutInitializer
@@ -484,11 +486,13 @@
    lateFieldTarget=_#_extension#1|finalUnnamedExtensionNonNullableWithoutInitializer
   */
   /*member: _extension#1|finalUnnamedExtensionNonNullableWithoutInitializer:
+   extensionName=<unnamed extension>.finalUnnamedExtensionNonNullableWithoutInitializer,
    lateFieldGetter,
    lateFieldName=_extension#1|finalUnnamedExtensionNonNullableWithoutInitializer,
    lateFieldTarget=_#_extension#1|finalUnnamedExtensionNonNullableWithoutInitializer
   */
   /*member: _extension#1|finalUnnamedExtensionNonNullableWithoutInitializer=:
+   extensionName=<unnamed extension>.finalUnnamedExtensionNonNullableWithoutInitializer,
    lateFieldName=_extension#1|finalUnnamedExtensionNonNullableWithoutInitializer,
    lateFieldSetter,
    lateFieldTarget=_#_extension#1|finalUnnamedExtensionNonNullableWithoutInitializer
@@ -505,11 +509,13 @@
    lateIsSetField
   */
   /*member: _extension#1|unnamedExtensionNullableWithoutInitializer=:
+   extensionName=<unnamed extension>.unnamedExtensionNullableWithoutInitializer,
    lateFieldName=_extension#1|unnamedExtensionNullableWithoutInitializer,
    lateFieldSetter,
    lateFieldTarget=_#_extension#1|unnamedExtensionNullableWithoutInitializer
   */
   /*member: _extension#1|unnamedExtensionNullableWithoutInitializer:
+   extensionName=<unnamed extension>.unnamedExtensionNullableWithoutInitializer,
    lateFieldGetter,
    lateFieldName=_extension#1|unnamedExtensionNullableWithoutInitializer,
    lateFieldTarget=_#_extension#1|unnamedExtensionNullableWithoutInitializer
@@ -526,11 +532,13 @@
    lateIsSetField
   */
   /*member: _extension#1|finalUnnamedExtensionNullableWithoutInitializer:
+   extensionName=<unnamed extension>.finalUnnamedExtensionNullableWithoutInitializer,
    lateFieldGetter,
    lateFieldName=_extension#1|finalUnnamedExtensionNullableWithoutInitializer,
    lateFieldTarget=_#_extension#1|finalUnnamedExtensionNullableWithoutInitializer
   */
   /*member: _extension#1|finalUnnamedExtensionNullableWithoutInitializer=:
+   extensionName=<unnamed extension>.finalUnnamedExtensionNullableWithoutInitializer,
    lateFieldName=_extension#1|finalUnnamedExtensionNullableWithoutInitializer,
    lateFieldSetter,
    lateFieldTarget=_#_extension#1|finalUnnamedExtensionNullableWithoutInitializer
@@ -543,12 +551,14 @@
    lateFieldTarget=_#_extension#1|unnamedExtensionNonNullableWithInitializer
   */
   /*member: _extension#1|unnamedExtensionNonNullableWithInitializer=:
+   extensionName=<unnamed extension>.unnamedExtensionNonNullableWithInitializer,
    lateFieldInitializer=13,
    lateFieldName=_extension#1|unnamedExtensionNonNullableWithInitializer,
    lateFieldSetter,
    lateFieldTarget=_#_extension#1|unnamedExtensionNonNullableWithInitializer
   */
   /*member: _extension#1|unnamedExtensionNonNullableWithInitializer:
+   extensionName=<unnamed extension>.unnamedExtensionNonNullableWithInitializer,
    lateFieldGetter,
    lateFieldInitializer=13,
    lateFieldName=_extension#1|unnamedExtensionNonNullableWithInitializer,
@@ -562,6 +572,7 @@
    lateFieldTarget=_#_extension#1|finalUnnamedExtensionNonNullableWithInitializer
   */
   /*member: _extension#1|finalUnnamedExtensionNonNullableWithInitializer:
+   extensionName=<unnamed extension>.finalUnnamedExtensionNonNullableWithInitializer,
    lateFieldGetter,
    lateFieldInitializer=14,
    lateFieldName=_extension#1|finalUnnamedExtensionNonNullableWithInitializer,
@@ -581,12 +592,14 @@
    lateIsSetField
   */
   /*member: _extension#1|unnamedExtensionNullableWithInitializer=:
+   extensionName=<unnamed extension>.unnamedExtensionNullableWithInitializer,
    lateFieldInitializer=15,
    lateFieldName=_extension#1|unnamedExtensionNullableWithInitializer,
    lateFieldSetter,
    lateFieldTarget=_#_extension#1|unnamedExtensionNullableWithInitializer
   */
   /*member: _extension#1|unnamedExtensionNullableWithInitializer:
+   extensionName=<unnamed extension>.unnamedExtensionNullableWithInitializer,
    lateFieldGetter,
    lateFieldInitializer=15,
    lateFieldName=_extension#1|unnamedExtensionNullableWithInitializer,
@@ -606,6 +619,7 @@
    lateIsSetField
   */
   /*member: _extension#1|finalUnnamedExtensionNullableWithInitializer:
+   extensionName=<unnamed extension>.finalUnnamedExtensionNullableWithInitializer,
    lateFieldGetter,
    lateFieldInitializer=16,
    lateFieldName=_extension#1|finalUnnamedExtensionNullableWithInitializer,
@@ -621,11 +635,13 @@
    lateFieldTarget=_#Extension|namedExtensionNonNullableWithoutInitializer
   */
   /*member: Extension|namedExtensionNonNullableWithoutInitializer=:
+   extensionName=Extension.namedExtensionNonNullableWithoutInitializer,
    lateFieldName=Extension|namedExtensionNonNullableWithoutInitializer,
    lateFieldSetter,
    lateFieldTarget=_#Extension|namedExtensionNonNullableWithoutInitializer
   */
   /*member: Extension|namedExtensionNonNullableWithoutInitializer:
+   extensionName=Extension.namedExtensionNonNullableWithoutInitializer,
    lateFieldGetter,
    lateFieldName=Extension|namedExtensionNonNullableWithoutInitializer,
    lateFieldTarget=_#Extension|namedExtensionNonNullableWithoutInitializer
@@ -637,11 +653,13 @@
    lateFieldTarget=_#Extension|finalNamedExtensionNonNullableWithoutInitializer
   */
   /*member: Extension|finalNamedExtensionNonNullableWithoutInitializer:
+   extensionName=Extension.finalNamedExtensionNonNullableWithoutInitializer,
    lateFieldGetter,
    lateFieldName=Extension|finalNamedExtensionNonNullableWithoutInitializer,
    lateFieldTarget=_#Extension|finalNamedExtensionNonNullableWithoutInitializer
   */
   /*member: Extension|finalNamedExtensionNonNullableWithoutInitializer=:
+   extensionName=Extension.finalNamedExtensionNonNullableWithoutInitializer,
    lateFieldName=Extension|finalNamedExtensionNonNullableWithoutInitializer,
    lateFieldSetter,
    lateFieldTarget=_#Extension|finalNamedExtensionNonNullableWithoutInitializer
@@ -658,11 +676,13 @@
    lateIsSetField
   */
   /*member: Extension|namedExtensionNullableWithoutInitializer:
+   extensionName=Extension.namedExtensionNullableWithoutInitializer,
    lateFieldGetter,
    lateFieldName=Extension|namedExtensionNullableWithoutInitializer,
    lateFieldTarget=_#Extension|namedExtensionNullableWithoutInitializer
   */
   /*member: Extension|namedExtensionNullableWithoutInitializer=:
+   extensionName=Extension.namedExtensionNullableWithoutInitializer,
    lateFieldName=Extension|namedExtensionNullableWithoutInitializer,
    lateFieldSetter,
    lateFieldTarget=_#Extension|namedExtensionNullableWithoutInitializer
@@ -679,11 +699,13 @@
    lateIsSetField
   */
   /*member: Extension|finalNamedExtensionNullableWithoutInitializer:
+   extensionName=Extension.finalNamedExtensionNullableWithoutInitializer,
    lateFieldGetter,
    lateFieldName=Extension|finalNamedExtensionNullableWithoutInitializer,
    lateFieldTarget=_#Extension|finalNamedExtensionNullableWithoutInitializer
   */
   /*member: Extension|finalNamedExtensionNullableWithoutInitializer=:
+   extensionName=Extension.finalNamedExtensionNullableWithoutInitializer,
    lateFieldName=Extension|finalNamedExtensionNullableWithoutInitializer,
    lateFieldSetter,
    lateFieldTarget=_#Extension|finalNamedExtensionNullableWithoutInitializer
@@ -696,12 +718,14 @@
    lateFieldTarget=_#Extension|namedExtensionNonNullableWithInitializer
   */
   /*member: Extension|namedExtensionNonNullableWithInitializer:
+   extensionName=Extension.namedExtensionNonNullableWithInitializer,
    lateFieldGetter,
    lateFieldInitializer=17,
    lateFieldName=Extension|namedExtensionNonNullableWithInitializer,
    lateFieldTarget=_#Extension|namedExtensionNonNullableWithInitializer
   */
   /*member: Extension|namedExtensionNonNullableWithInitializer=:
+   extensionName=Extension.namedExtensionNonNullableWithInitializer,
    lateFieldInitializer=17,
    lateFieldName=Extension|namedExtensionNonNullableWithInitializer,
    lateFieldSetter,
@@ -715,6 +739,7 @@
    lateFieldTarget=_#Extension|finalNamedExtensionNonNullableWithInitializer
   */
   /*member: Extension|finalNamedExtensionNonNullableWithInitializer:
+   extensionName=Extension.finalNamedExtensionNonNullableWithInitializer,
    lateFieldGetter,
    lateFieldInitializer=18,
    lateFieldName=Extension|finalNamedExtensionNonNullableWithInitializer,
@@ -734,12 +759,14 @@
    lateIsSetField
   */
   /*member: Extension|namedExtensionNullableWithInitializer:
+   extensionName=Extension.namedExtensionNullableWithInitializer,
    lateFieldGetter,
    lateFieldInitializer=19,
    lateFieldName=Extension|namedExtensionNullableWithInitializer,
    lateFieldTarget=_#Extension|namedExtensionNullableWithInitializer
   */
   /*member: Extension|namedExtensionNullableWithInitializer=:
+   extensionName=Extension.namedExtensionNullableWithInitializer,
    lateFieldInitializer=19,
    lateFieldName=Extension|namedExtensionNullableWithInitializer,
    lateFieldSetter,
@@ -759,6 +786,7 @@
    lateIsSetField
   */
   /*member: Extension|finalNamedExtensionNullableWithInitializer:
+   extensionName=Extension.finalNamedExtensionNullableWithInitializer,
    lateFieldGetter,
    lateFieldInitializer=20,
    lateFieldName=Extension|finalNamedExtensionNullableWithInitializer,
diff --git a/pkg/front_end/test/predicates/data/late_names.dart b/pkg/front_end/test/predicates/data/late_names.dart
index c6dd3b4..7e17665 100644
--- a/pkg/front_end/test/predicates/data/late_names.dart
+++ b/pkg/front_end/test/predicates/data/late_names.dart
@@ -348,11 +348,13 @@
    lateIsSetField
   */
   /*member: _extension#1|a:
+   extensionName=<unnamed extension>.a,
    lateFieldGetter,
    lateFieldName=_extension#1|a,
    lateFieldTarget=_#_extension#1|a
   */
   /*member: _extension#1|a=:
+   extensionName=<unnamed extension>.a,
    lateFieldName=_extension#1|a,
    lateFieldSetter,
    lateFieldTarget=_#_extension#1|a
@@ -369,11 +371,13 @@
    lateIsSetField
   */
   /*member: _extension#1|aa:
+   extensionName=<unnamed extension>.aa,
    lateFieldGetter,
    lateFieldName=_extension#1|aa,
    lateFieldTarget=_#_extension#1|aa
   */
   /*member: _extension#1|aa=:
+   extensionName=<unnamed extension>.aa,
    lateFieldName=_extension#1|aa,
    lateFieldSetter,
    lateFieldTarget=_#_extension#1|aa
@@ -390,11 +394,13 @@
    lateIsSetField
   */
   /*member: _extension#1|_b:
+   extensionName=<unnamed extension>._b,
    lateFieldGetter,
    lateFieldName=_extension#1|_b,
    lateFieldTarget=_#_extension#1|_b
   */
   /*member: _extension#1|_b=:
+   extensionName=<unnamed extension>._b,
    lateFieldName=_extension#1|_b,
    lateFieldSetter,
    lateFieldTarget=_#_extension#1|_b
@@ -414,11 +420,13 @@
    lateIsSetField
   */
   /*member: _Extension|a:
+   extensionName=_Extension.a,
    lateFieldGetter,
    lateFieldName=_Extension|a,
    lateFieldTarget=_#_Extension|a
   */
   /*member: _Extension|a=:
+   extensionName=_Extension.a,
    lateFieldName=_Extension|a,
    lateFieldSetter,
    lateFieldTarget=_#_Extension|a
@@ -435,11 +443,13 @@
    lateIsSetField
   */
   /*member: _Extension|aa:
+   extensionName=_Extension.aa,
    lateFieldGetter,
    lateFieldName=_Extension|aa,
    lateFieldTarget=_#_Extension|aa
   */
   /*member: _Extension|aa=:
+   extensionName=_Extension.aa,
    lateFieldName=_Extension|aa,
    lateFieldSetter,
    lateFieldTarget=_#_Extension|aa
@@ -456,11 +466,13 @@
    lateIsSetField
   */
   /*member: _Extension|_b:
+   extensionName=_Extension._b,
    lateFieldGetter,
    lateFieldName=_Extension|_b,
    lateFieldTarget=_#_Extension|_b
   */
   /*member: _Extension|_b=:
+   extensionName=_Extension._b,
    lateFieldName=_Extension|_b,
    lateFieldSetter,
    lateFieldTarget=_#_Extension|_b
diff --git a/pkg/front_end/test/predicates/predicate_test.dart b/pkg/front_end/test/predicates/predicate_test.dart
index 134f29e..64df7eb 100644
--- a/pkg/front_end/test/predicates/predicate_test.dart
+++ b/pkg/front_end/test/predicates/predicate_test.dart
@@ -65,6 +65,7 @@
   static const String lateLocalSetter = 'lateLocalSetter';
 
   static const String extensionThis = 'extensionThis';
+  static const String extensionName = 'extensionName';
 
   static const String tearoffLowering = 'tearoffLowering';
   static const String tearoffConstructor = 'tearoffConstructor';
@@ -162,6 +163,10 @@
       if (isTypedefTearOffLowering(node)) {
         features.add(Tags.tearoffTypedef);
       }
+      if (node.isExtensionMember || node.isExtensionTypeMember) {
+        features[Tags.extensionName] =
+            extractQualifiedNameFromExtensionMember(node)!;
+      }
     }
     if (isTearOffLowering(node)) {
       features.add(Tags.tearoffLowering);
diff --git a/pkg/front_end/testcases/general/dynamic_modules/main.dart.strong.expect b/pkg/front_end/testcases/general/dynamic_modules/main.dart.strong.expect
index 088db18..b447317 100644
--- a/pkg/front_end/testcases/general/dynamic_modules/main.dart.strong.expect
+++ b/pkg/front_end/testcases/general/dynamic_modules/main.dart.strong.expect
@@ -175,13 +175,13 @@
 //   print(ExtType1);
 //         ^
 //
-// pkg/front_end/testcases/general/dynamic_modules/main.dart:130:9: Error: Cannot invoke member 'ExtType1.constructor#' from a dynamic module.
-// Try removing the call or update the dynamic interface to list member 'ExtType1.constructor#' as callable.
+// pkg/front_end/testcases/general/dynamic_modules/main.dart:130:9: Error: Cannot invoke member 'ExtType1.new' from a dynamic module.
+// Try removing the call or update the dynamic interface to list member 'ExtType1.new' as callable.
 //   print(ExtType1(42));
 //         ^
 //
-// pkg/front_end/testcases/general/dynamic_modules/main.dart:131:12: Error: Cannot invoke member 'Ext1.get#isPositive' from a dynamic module.
-// Try removing the call or update the dynamic interface to list member 'Ext1.get#isPositive' as callable.
+// pkg/front_end/testcases/general/dynamic_modules/main.dart:131:12: Error: Cannot invoke member 'Ext1.isPositive' from a dynamic module.
+// Try removing the call or update the dynamic interface to list member 'Ext1.isPositive' as callable.
 //   print(42.isPositive);
 //            ^
 //
diff --git a/pkg/front_end/testcases/general/dynamic_modules/main.dart.strong.modular.expect b/pkg/front_end/testcases/general/dynamic_modules/main.dart.strong.modular.expect
index b27f867..3e6db6c 100644
--- a/pkg/front_end/testcases/general/dynamic_modules/main.dart.strong.modular.expect
+++ b/pkg/front_end/testcases/general/dynamic_modules/main.dart.strong.modular.expect
@@ -175,13 +175,13 @@
 //   print(ExtType1);
 //         ^
 //
-// pkg/front_end/testcases/general/dynamic_modules/main.dart:130:9: Error: Cannot invoke member 'ExtType1.constructor#' from a dynamic module.
-// Try removing the call or update the dynamic interface to list member 'ExtType1.constructor#' as callable.
+// pkg/front_end/testcases/general/dynamic_modules/main.dart:130:9: Error: Cannot invoke member 'ExtType1.new' from a dynamic module.
+// Try removing the call or update the dynamic interface to list member 'ExtType1.new' as callable.
 //   print(ExtType1(42));
 //         ^
 //
-// pkg/front_end/testcases/general/dynamic_modules/main.dart:131:12: Error: Cannot invoke member 'Ext1.get#isPositive' from a dynamic module.
-// Try removing the call or update the dynamic interface to list member 'Ext1.get#isPositive' as callable.
+// pkg/front_end/testcases/general/dynamic_modules/main.dart:131:12: Error: Cannot invoke member 'Ext1.isPositive' from a dynamic module.
+// Try removing the call or update the dynamic interface to list member 'Ext1.isPositive' as callable.
 //   print(42.isPositive);
 //            ^
 //
diff --git a/pkg/front_end/testcases/general/dynamic_modules/main.dart.strong.transformed.expect b/pkg/front_end/testcases/general/dynamic_modules/main.dart.strong.transformed.expect
index f8cb953..b4c1ad4 100644
--- a/pkg/front_end/testcases/general/dynamic_modules/main.dart.strong.transformed.expect
+++ b/pkg/front_end/testcases/general/dynamic_modules/main.dart.strong.transformed.expect
@@ -175,13 +175,13 @@
 //   print(ExtType1);
 //         ^
 //
-// pkg/front_end/testcases/general/dynamic_modules/main.dart:130:9: Error: Cannot invoke member 'ExtType1.constructor#' from a dynamic module.
-// Try removing the call or update the dynamic interface to list member 'ExtType1.constructor#' as callable.
+// pkg/front_end/testcases/general/dynamic_modules/main.dart:130:9: Error: Cannot invoke member 'ExtType1.new' from a dynamic module.
+// Try removing the call or update the dynamic interface to list member 'ExtType1.new' as callable.
 //   print(ExtType1(42));
 //         ^
 //
-// pkg/front_end/testcases/general/dynamic_modules/main.dart:131:12: Error: Cannot invoke member 'Ext1.get#isPositive' from a dynamic module.
-// Try removing the call or update the dynamic interface to list member 'Ext1.get#isPositive' as callable.
+// pkg/front_end/testcases/general/dynamic_modules/main.dart:131:12: Error: Cannot invoke member 'Ext1.isPositive' from a dynamic module.
+// Try removing the call or update the dynamic interface to list member 'Ext1.isPositive' as callable.
 //   print(42.isPositive);
 //            ^
 //