Extension types. Support in InheritanceManager3.

Change-Id: I49e30138f810247d1e20fa983f33f32d99a29c62
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/316845
Reviewed-by: Samuel Rawlins <srawlins@google.com>
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
diff --git a/pkg/analyzer/lib/src/dart/element/extensions.dart b/pkg/analyzer/lib/src/dart/element/extensions.dart
index 725ef73..4ab7e2a3 100644
--- a/pkg/analyzer/lib/src/dart/element/extensions.dart
+++ b/pkg/analyzer/lib/src/dart/element/extensions.dart
@@ -104,6 +104,12 @@
   bool get isEnumConstructor {
     return this is ConstructorElement && enclosingElement2 is EnumElementImpl;
   }
+
+  /// Whether the enclosing element is the class `Object`.
+  bool get isObjectMember {
+    final enclosing = enclosingElement2;
+    return enclosing is ClassElement && enclosing.isDartCoreObject;
+  }
 }
 
 extension ExecutableElementExtensionQuestion on ExecutableElement? {
diff --git a/pkg/analyzer/lib/src/dart/element/inheritance_manager3.dart b/pkg/analyzer/lib/src/dart/element/inheritance_manager3.dart
index b4d417a..be92357 100644
--- a/pkg/analyzer/lib/src/dart/element/inheritance_manager3.dart
+++ b/pkg/analyzer/lib/src/dart/element/inheritance_manager3.dart
@@ -17,9 +17,9 @@
   final List<ExecutableElement> candidates;
 
   CandidatesConflict({
-    required Name name,
+    required super.name,
     required this.candidates,
-  }) : super(name);
+  });
 }
 
 /// Failure to find a valid signature from superinterfaces.
@@ -27,7 +27,9 @@
   /// The name for which we failed to find a valid signature.
   final Name name;
 
-  Conflict(this.name);
+  Conflict({
+    required this.name,
+  });
 }
 
 /// Failure because of a getter and a method from direct superinterfaces.
@@ -36,10 +38,10 @@
   final ExecutableElement method;
 
   GetterMethodConflict({
-    required Name name,
+    required super.name,
     required this.getter,
     required this.method,
-  }) : super(name);
+  });
 }
 
 /// Manages knowledge about interface types and their members.
@@ -160,7 +162,7 @@
   Map<Name, ExecutableElement> getInheritedConcreteMap2(
       InterfaceElement element) {
     var interface = getInterface(element);
-    return interface._superImplemented.last;
+    return interface.superImplemented.last;
   }
 
   /// Return the mapping from names to most specific signatures of members
@@ -189,13 +191,13 @@
   /// corresponding name will not be included.
   Map<Name, ExecutableElement> getInheritedMap2(InterfaceElement element) {
     var interface = getInterface(element);
-    var inheritedMap = interface._inheritedMap;
+    var inheritedMap = interface.inheritedMap;
     if (inheritedMap == null) {
-      inheritedMap = interface._inheritedMap = {};
+      inheritedMap = interface.inheritedMap = {};
       _findMostSpecificFromNamedCandidates(
         element,
         inheritedMap,
-        interface._overridden,
+        interface.overridden,
         doTopMerge: false,
       );
     }
@@ -216,7 +218,9 @@
     }
 
     try {
-      if (element is MixinElement) {
+      if (element is ExtensionTypeElement) {
+        result = _getInterfaceExtensionType(element);
+      } else if (element is MixinElement) {
         result = _getInterfaceMixin(element);
       } else {
         result = _getInterfaceClass(element);
@@ -272,7 +276,10 @@
   }) {
     var interface = getInterface(element);
     if (forSuper) {
-      var superImplemented = interface._superImplemented;
+      if (element is ExtensionTypeElement) {
+        return null;
+      }
+      var superImplemented = interface.superImplemented;
       if (forMixinIndex >= 0) {
         return superImplemented[forMixinIndex][name];
       }
@@ -302,7 +309,7 @@
   /// if no members would be overridden.
   List<ExecutableElement>? getOverridden2(InterfaceElement element, Name name) {
     var interface = getInterface(element);
-    return interface._overridden[name];
+    return interface.overridden[name];
   }
 
   /// Remove interfaces for classes defined in specified libraries.
@@ -608,12 +615,12 @@
     var noSuchMethodForwarders = <Name>{};
     if (element is ClassElement && element.isAbstract) {
       if (superTypeInterface != null) {
-        noSuchMethodForwarders = superTypeInterface._noSuchMethodForwarders;
+        noSuchMethodForwarders = superTypeInterface.noSuchMethodForwarders;
       }
     } else {
       var noSuchMethod = implemented[_noSuchMethodName];
       if (noSuchMethod != null && !_isDeclaredInObject(noSuchMethod)) {
-        var superForwarders = superTypeInterface?._noSuchMethodForwarders;
+        var superForwarders = superTypeInterface?.noSuchMethodForwarders;
         for (var entry in interface.entries) {
           var name = entry.key;
           if (!implemented.containsKey(name) ||
@@ -649,6 +656,126 @@
     );
   }
 
+  /// See https://github.com/dart-lang/language
+  ///   blob/main/accepted/future-releases/extension-types/feature-specification.md
+  ///   #static-analysis-of-an-extension-type-member-invocation
+  ///
+  /// We handle "has an extension type member" and "has a non-extension type
+  /// member" portions, considering redeclaration and conflicts.
+  Interface _getInterfaceExtensionType(ExtensionTypeElement element) {
+    final augmented = element.augmentedOfDeclaration;
+
+    // Add instance members implemented by the element itself.
+    final declared = <Name, ExecutableElement>{};
+    _addImplemented(declared, element, augmented);
+
+    // These declared members take precedence over "inherited" ones.
+    final implemented = Map.of(declared);
+
+    // Prepare candidates for inheritance.
+    final extensionCandidates = <Name, List<ExecutableElement>>{};
+    final notExtensionCandidates = <Name, List<ExecutableElement>>{};
+    for (final interface in augmented.interfaces) {
+      for (final entry in getInterface(interface.element).map.entries) {
+        final name = entry.key;
+        if (interface.element is ExtensionTypeElement) {
+          (extensionCandidates[name] ??= []).add(entry.value);
+        } else {
+          (notExtensionCandidates[name] ??= []).add(entry.value);
+        }
+      }
+    }
+
+    final overridden = <Name, List<ExecutableElement>>{};
+    final conflicts = <Conflict>[];
+
+    // Add extension type members.
+    for (final entry in extensionCandidates.entries) {
+      final name = entry.key;
+      final candidates = entry.value;
+      overridden[name] = candidates;
+
+      // Stop if redeclared.
+      if (implemented.containsKey(name)) {
+        continue;
+      }
+
+      // The inherited member must be unique.
+      ExecutableElement? uniqueElement;
+      for (final candidate in candidates) {
+        if (uniqueElement == null) {
+          uniqueElement = candidate;
+        } else if (uniqueElement.declaration != candidate.declaration) {
+          uniqueElement = null;
+          break;
+        }
+      }
+
+      if (uniqueElement != null) {
+        implemented[name] = uniqueElement;
+      } else {
+        conflicts.add(
+          NotUniqueExtensionMemberConflict(
+            name: name,
+            candidates: candidates,
+          ),
+        );
+      }
+    }
+
+    // Add non-extension type members.
+    for (final entry in notExtensionCandidates.entries) {
+      final name = entry.key;
+      final candidates = entry.value;
+      (overridden[name] ??= []).addAll(candidates);
+
+      // Stop if redeclared.
+      if (implemented.containsKey(name)) {
+        continue;
+      }
+
+      final combinedSignature = combineSignatures(
+        targetClass: element,
+        candidates: candidates,
+        doTopMerge: true,
+        name: name,
+      );
+
+      if (combinedSignature != null) {
+        implemented[name] = combinedSignature;
+      } else {
+        conflicts.add(
+          CandidatesConflict(
+            name: name,
+            candidates: candidates,
+          ),
+        );
+      }
+    }
+
+    // Ensure unique overridden elements.
+    final overridden2 = <Name, List<ExecutableElement>>{};
+    for (final entry in overridden.entries) {
+      final name = entry.key;
+      final elements = entry.value;
+      if (elements.length == 1) {
+        overridden2[name] = elements;
+      } else {
+        overridden2[name] = elements.toSet().toFixedList();
+      }
+    }
+
+    return Interface._(
+      implemented,
+      declared,
+      implemented,
+      const {},
+      overridden2,
+      const [],
+      conflicts.toFixedList(),
+    );
+  }
+
   Interface _getInterfaceMixin(MixinElement element) {
     final augmented = element.augmentedOfDeclaration;
     var classLibrary = element.library;
@@ -941,17 +1068,17 @@
   final Map<Name, ExecutableElement> implemented;
 
   /// The set of names that are `noSuchMethod` forwarders in [implemented].
-  final Set<Name> _noSuchMethodForwarders;
+  final Set<Name> noSuchMethodForwarders;
 
   /// The map of names to their signatures from the mixins, superclasses,
   /// or interfaces.
-  final Map<Name, List<ExecutableElement>> _overridden;
+  final Map<Name, List<ExecutableElement>> overridden;
 
   /// Each item of this list maps names to their concrete implementations.
   /// The first item of the list is the nominal superclass, next the nominal
   /// superclass plus the first mixin, etc. So, for the class like
   /// `class C extends S with M1, M2`, we get `[S, S&M1, S&M1&M2]`.
-  final List<Map<Name, ExecutableElement>> _superImplemented;
+  final List<Map<Name, ExecutableElement>> superImplemented;
 
   /// The list of conflicts between superinterfaces - the nominal superclass,
   /// mixins, and interfaces.  Does not include conflicts with the declared
@@ -960,21 +1087,21 @@
 
   /// The map of names to the most specific signatures from the mixins,
   /// superclasses, or interfaces.
-  Map<Name, ExecutableElement>? _inheritedMap;
+  Map<Name, ExecutableElement>? inheritedMap;
 
   Interface._(
     this.map,
     this.declared,
     this.implemented,
-    this._noSuchMethodForwarders,
-    this._overridden,
-    this._superImplemented,
+    this.noSuchMethodForwarders,
+    this.overridden,
+    this.superImplemented,
     this.conflicts,
   );
 
   /// Return `true` if the [name] is implemented in the supertype.
   bool isSuperImplemented(Name name) {
-    return _superImplemented.last.containsKey(name);
+    return superImplemented.last.containsKey(name);
   }
 }
 
@@ -1022,6 +1149,16 @@
   String toString() => libraryUri != null ? '$libraryUri::$name' : name;
 }
 
+/// Failure because of not unique extension type member.
+class NotUniqueExtensionMemberConflict extends Conflict {
+  final List<ExecutableElement> candidates;
+
+  NotUniqueExtensionMemberConflict({
+    required super.name,
+    required this.candidates,
+  });
+}
+
 class _ParameterDesc {
   final int? index;
   final String? name;
diff --git a/pkg/analyzer/lib/src/fasta/ast_builder.dart b/pkg/analyzer/lib/src/fasta/ast_builder.dart
index 75ad09a..ab09464 100644
--- a/pkg/analyzer/lib/src/fasta/ast_builder.dart
+++ b/pkg/analyzer/lib/src/fasta/ast_builder.dart
@@ -341,6 +341,9 @@
 
   @override
   void beginExtensionTypeDeclaration(Token extensionKeyword, Token name) {
+    assert(optional('extension', extensionKeyword));
+    assert(_classLikeBuilder == null);
+
     final typeParameters = pop() as TypeParameterListImpl?;
     final metadata = pop() as List<AnnotationImpl>?;
     final comment = _findComment(metadata, extensionKeyword);
@@ -1669,6 +1672,8 @@
         implementsClause: implementsClause,
       ),
     );
+
+    _classLikeBuilder = null;
   }
 
   @override
diff --git a/pkg/analyzer/lib/src/summary2/types_builder.dart b/pkg/analyzer/lib/src/summary2/types_builder.dart
index d344889..0f0f555 100644
--- a/pkg/analyzer/lib/src/summary2/types_builder.dart
+++ b/pkg/analyzer/lib/src/summary2/types_builder.dart
@@ -262,17 +262,21 @@
     final element = node.declaredElement!;
     final typeSystem = element.library.typeSystem;
 
-    final interfaces = node.implementsClause?.interfaces
+    final type = node.representation.fieldType.typeOrThrow;
+    element.representation.type = type;
+
+    var interfaces = node.implementsClause?.interfaces
         .map((e) => e.type)
         .whereType<InterfaceType>()
         .where(typeSystem.isValidExtensionTypeSuperinterface)
         .toFixedList();
-    if (interfaces != null) {
-      element.interfaces = interfaces;
+    if (interfaces == null || interfaces.isEmpty) {
+      final superInterface = typeSystem.isNonNullable(type)
+          ? typeSystem.objectNone
+          : typeSystem.objectQuestion;
+      interfaces = [superInterface];
     }
-
-    final type = node.representation.fieldType.typeOrThrow;
-    element.representation.type = type;
+    element.interfaces = interfaces;
 
     final primaryConstructor = element.constructors.first;
     final primaryFormalParameter = primaryConstructor.parameters.first;
diff --git a/pkg/analyzer/test/src/dart/element/inheritance_manager3_test.dart b/pkg/analyzer/test/src/dart/element/inheritance_manager3_test.dart
index ab961fa..b8d6c7e 100644
--- a/pkg/analyzer/test/src/dart/element/inheritance_manager3_test.dart
+++ b/pkg/analyzer/test/src/dart/element/inheritance_manager3_test.dart
@@ -5,16 +5,24 @@
 import 'package:analyzer/dart/element/element.dart';
 import 'package:analyzer/dart/element/nullability_suffix.dart';
 import 'package:analyzer/dart/element/type.dart';
+import 'package:analyzer/src/dart/element/element.dart';
+import 'package:analyzer/src/dart/element/extensions.dart';
 import 'package:analyzer/src/dart/element/inheritance_manager3.dart';
+import 'package:collection/collection.dart';
 import 'package:test/test.dart';
 import 'package:test_reflective_loader/test_reflective_loader.dart';
 
+import '../../../util/element_printer.dart';
+import '../../../util/tree_string_sink.dart';
+import '../../summary/elements_base.dart';
 import '../resolution/context_collection_resolution.dart';
+import '../resolution/node_text_expectations.dart';
 
 main() {
   defineReflectiveSuite(() {
     defineReflectiveTests(InheritanceManager3Test);
     defineReflectiveTests(InheritanceManager3WithoutNullSafetyTest);
+    defineReflectiveTests(InheritanceManager3Test_ExtensionType);
   });
 }
 
@@ -401,6 +409,585 @@
 }
 
 @reflectiveTest
+class InheritanceManager3Test_ExtensionType extends ElementsBaseTest {
+  final printerConfiguration = _InstancePrinterConfiguration();
+
+  @override
+  bool get keepLinkingLibraries => true;
+
+  void assertInterfaceText(InterfaceElementImpl element, String expected) {
+    final actual = _interfaceText(element);
+    if (actual != expected) {
+      print('-------- Actual --------');
+      print('$actual------------------------');
+      NodeTextExpectationsCollector.add(actual);
+    }
+    expect(actual, expected);
+  }
+
+  @override
+  void setUp() {
+    super.setUp();
+    printerConfiguration.withoutIdenticalImplemented = true;
+  }
+
+  test_declareGetter() async {
+    final library = await buildLibrary(r'''
+extension type A(int it) {
+  int get foo => 0;
+}
+''');
+
+    final element = library.extensionType('A');
+    assertInterfaceText(element, r'''
+map
+  foo: self::@extensionType::A::@getter::foo
+  it: self::@extensionType::A::@getter::it
+declared
+  foo: self::@extensionType::A::@getter::foo
+  it: self::@extensionType::A::@getter::it
+''');
+  }
+
+  test_declareMethod() async {
+    final library = await buildLibrary(r'''
+extension type A(int it) {
+  void foo() {}
+}
+''');
+
+    final element = library.extensionType('A');
+    assertInterfaceText(element, r'''
+map
+  foo: self::@extensionType::A::@method::foo
+  it: self::@extensionType::A::@getter::it
+declared
+  foo: self::@extensionType::A::@method::foo
+  it: self::@extensionType::A::@getter::it
+''');
+  }
+
+  test_declareMethod_implementClass_method2_wouldConflict() async {
+    final library = await buildLibrary(r'''
+class A {
+  int foo() => 0;
+}
+
+class B {
+  String foo() => '0';
+}
+
+extension type C(Object it) implements A, B {
+  void foo() {}
+}
+''');
+
+    final element = library.extensionType('C');
+    assertInterfaceText(element, r'''
+map
+  foo: self::@extensionType::C::@method::foo
+  it: self::@extensionType::C::@getter::it
+declared
+  foo: self::@extensionType::C::@method::foo
+  it: self::@extensionType::C::@getter::it
+overridden
+  foo
+    self::@class::A::@method::foo
+    self::@class::B::@method::foo
+''');
+  }
+
+  test_declareMethod_implementClass_noOverride() async {
+    final library = await buildLibrary(r'''
+class A {}
+
+class B extends A {
+  void foo() {}
+}
+
+extension type C(B it) implements A {
+  void foo() {}
+}
+''');
+
+    final element = library.extensionType('C');
+    assertInterfaceText(element, r'''
+map
+  foo: self::@extensionType::C::@method::foo
+  it: self::@extensionType::C::@getter::it
+declared
+  foo: self::@extensionType::C::@method::foo
+  it: self::@extensionType::C::@getter::it
+''');
+  }
+
+  test_declareMethod_implementClass_override() async {
+    final library = await buildLibrary(r'''
+class A {
+  void foo() {}
+}
+
+class B extends A {
+  void bar() {}
+}
+
+extension type C(B it) implements A {
+  void foo() {}
+}
+''');
+
+    final element = library.extensionType('C');
+    assertInterfaceText(element, r'''
+map
+  foo: self::@extensionType::C::@method::foo
+  it: self::@extensionType::C::@getter::it
+declared
+  foo: self::@extensionType::C::@method::foo
+  it: self::@extensionType::C::@getter::it
+overridden
+  foo
+    self::@class::A::@method::foo
+''');
+  }
+
+  test_declareMethod_implementClass_override_getter() async {
+    final library = await buildLibrary(r'''
+class A {
+  int get foo => 0;
+}
+
+class B extends A {
+  void bar() {}
+}
+
+extension type C(B it) implements A {
+  void foo() {}
+}
+''');
+
+    final element = library.extensionType('C');
+    assertInterfaceText(element, r'''
+map
+  foo: self::@extensionType::C::@method::foo
+  it: self::@extensionType::C::@getter::it
+declared
+  foo: self::@extensionType::C::@method::foo
+  it: self::@extensionType::C::@getter::it
+overridden
+  foo
+    self::@class::A::@getter::foo
+''');
+  }
+
+  test_declareMethod_implementExtensionType_method2_wouldConflict() async {
+    final library = await buildLibrary(r'''
+extension type A1(int it) {
+  void foo() {}
+}
+
+extension type A2(int it) {
+  void foo() {}
+}
+
+extension type B(int it) implements A1, A2 {
+  void foo() {}
+}
+''');
+
+    final element = library.extensionType('B');
+    assertInterfaceText(element, r'''
+map
+  foo: self::@extensionType::B::@method::foo
+  it: self::@extensionType::B::@getter::it
+declared
+  foo: self::@extensionType::B::@method::foo
+  it: self::@extensionType::B::@getter::it
+overridden
+  foo
+    self::@extensionType::A1::@method::foo
+    self::@extensionType::A2::@method::foo
+  it
+    self::@extensionType::A1::@getter::it
+    self::@extensionType::A2::@getter::it
+''');
+  }
+
+  test_declareMethod_implementExtensionType_override() async {
+    final library = await buildLibrary(r'''
+extension type A(int it) {
+  void foo() {}
+}
+
+extension type B(int it) implements A {
+  void foo() {}
+}
+''');
+
+    final element = library.extensionType('B');
+    assertInterfaceText(element, r'''
+map
+  foo: self::@extensionType::B::@method::foo
+  it: self::@extensionType::B::@getter::it
+declared
+  foo: self::@extensionType::B::@method::foo
+  it: self::@extensionType::B::@getter::it
+overridden
+  foo
+    self::@extensionType::A::@method::foo
+  it
+    self::@extensionType::A::@getter::it
+''');
+  }
+
+  test_declareMethod_implementExtensionType_override_getter() async {
+    final library = await buildLibrary(r'''
+extension type A(int it) {
+  int get foo => 0;
+}
+
+extension type B(int it) implements A {
+  void foo() {}
+}
+''');
+
+    final element = library.extensionType('B');
+    assertInterfaceText(element, r'''
+map
+  foo: self::@extensionType::B::@method::foo
+  it: self::@extensionType::B::@getter::it
+declared
+  foo: self::@extensionType::B::@method::foo
+  it: self::@extensionType::B::@getter::it
+overridden
+  foo
+    self::@extensionType::A::@getter::foo
+  it
+    self::@extensionType::A::@getter::it
+''');
+  }
+
+  test_declareMethod_static() async {
+    final library = await buildLibrary(r'''
+extension type A(int it) {
+  static void foo() {}
+}
+''');
+
+    final element = library.extensionType('A');
+    assertInterfaceText(element, r'''
+map
+  it: self::@extensionType::A::@getter::it
+declared
+  it: self::@extensionType::A::@getter::it
+''');
+  }
+
+  test_noDeclaration_implementClass_implementExtensionType() async {
+    final library = await buildLibrary(r'''
+extension type A(int it) {
+  void foo() {}
+}
+
+class B {
+  void foo() {}
+}
+
+class C extends B {}
+
+extension type D(C it) implements B, A {}
+''');
+
+    final element = library.extensionType('D');
+    assertInterfaceText(element, r'''
+map
+  foo: self::@extensionType::A::@method::foo
+  it: self::@extensionType::D::@getter::it
+declared
+  it: self::@extensionType::D::@getter::it
+overridden
+  foo
+    self::@extensionType::A::@method::foo
+    self::@class::B::@method::foo
+  it
+    self::@extensionType::A::@getter::it
+''');
+  }
+
+  test_noDeclaration_implementClass_method() async {
+    final library = await buildLibrary(r'''
+class A {
+  void foo() {}
+}
+
+class B extends A {}
+
+extension type C(B it) implements A {}
+''');
+
+    final element = library.extensionType('C');
+    assertInterfaceText(element, r'''
+map
+  foo: self::@class::A::@method::foo
+  it: self::@extensionType::C::@getter::it
+declared
+  it: self::@extensionType::C::@getter::it
+overridden
+  foo
+    self::@class::A::@method::foo
+''');
+  }
+
+  test_noDeclaration_implementClass_method2_hasConflict() async {
+    final library = await buildLibrary(r'''
+class A {
+  int foo() => 0;
+}
+
+class B {
+  String foo() => '0';
+}
+
+extension type C(Object it) implements A, B {}
+''');
+
+    final element = library.extensionType('C');
+    assertInterfaceText(element, r'''
+map
+  it: self::@extensionType::C::@getter::it
+declared
+  it: self::@extensionType::C::@getter::it
+overridden
+  foo
+    self::@class::A::@method::foo
+    self::@class::B::@method::foo
+conflicts
+  CandidatesConflict
+    self::@class::A::@method::foo
+    self::@class::B::@method::foo
+''');
+  }
+
+  test_noDeclaration_implementClass_method2_noConflict() async {
+    final library = await buildLibrary(r'''
+class A {
+  int foo() => 0;
+}
+
+class B {
+  num foo() => 0;
+}
+
+extension type C(Object it) implements A, B {}
+''');
+
+    final element = library.extensionType('C');
+    assertInterfaceText(element, r'''
+map
+  foo: self::@class::A::@method::foo
+  it: self::@extensionType::C::@getter::it
+declared
+  it: self::@extensionType::C::@getter::it
+overridden
+  foo
+    self::@class::A::@method::foo
+    self::@class::B::@method::foo
+''');
+  }
+
+  test_noDeclaration_implementClass_method2_noConflict2() async {
+    final library = await buildLibrary(r'''
+class A {
+  int foo() => 0;
+}
+
+class B1 extends A {}
+
+class B2 extends A {}
+
+extension type C(Object it) implements B1, B2 {}
+''');
+
+    final element = library.extensionType('C');
+    assertInterfaceText(element, r'''
+map
+  foo: self::@class::A::@method::foo
+  it: self::@extensionType::C::@getter::it
+declared
+  it: self::@extensionType::C::@getter::it
+overridden
+  foo
+    self::@class::A::@method::foo
+''');
+  }
+
+  test_noDeclaration_implementClass_setter() async {
+    final library = await buildLibrary(r'''
+class A {
+  set foo(int _) {}
+}
+
+class B extends A {}
+
+extension type C(B it) implements A {}
+''');
+
+    final element = library.extensionType('C');
+    assertInterfaceText(element, r'''
+map
+  foo=: self::@class::A::@setter::foo
+  it: self::@extensionType::C::@getter::it
+declared
+  it: self::@extensionType::C::@getter::it
+overridden
+  foo=
+    self::@class::A::@setter::foo
+''');
+  }
+
+  test_noDeclaration_implementExtensionType_method() async {
+    final library = await buildLibrary(r'''
+extension type A(int it) {
+  void foo() {}
+}
+
+extension type B(int it) implements A {}
+''');
+
+    final element = library.extensionType('B');
+    assertInterfaceText(element, r'''
+map
+  foo: self::@extensionType::A::@method::foo
+  it: self::@extensionType::B::@getter::it
+declared
+  it: self::@extensionType::B::@getter::it
+overridden
+  foo
+    self::@extensionType::A::@method::foo
+  it
+    self::@extensionType::A::@getter::it
+''');
+  }
+
+  test_noDeclaration_implementExtensionType_method2_hasConflict() async {
+    final library = await buildLibrary(r'''
+extension type A1(int it) {
+  void foo() {}
+}
+
+extension type A2(int it) {
+  void foo() {}
+}
+
+extension type B(int it) implements A1, A2 {}
+''');
+
+    final element = library.extensionType('B');
+    assertInterfaceText(element, r'''
+map
+  it: self::@extensionType::B::@getter::it
+declared
+  it: self::@extensionType::B::@getter::it
+overridden
+  foo
+    self::@extensionType::A1::@method::foo
+    self::@extensionType::A2::@method::foo
+  it
+    self::@extensionType::A1::@getter::it
+    self::@extensionType::A2::@getter::it
+conflicts
+  NotUniqueExtensionMemberConflict
+    self::@extensionType::A1::@method::foo
+    self::@extensionType::A2::@method::foo
+''');
+  }
+
+  test_noDeclaration_implementExtensionType_method2_noConflict() async {
+    final library = await buildLibrary(r'''
+extension type A(int it) {
+  void foo() {}
+}
+
+extension type B1(int it) implements A {}
+
+extension type B2(int it) implements A {}
+
+extension type C(int it) implements B1, B2 {}
+''');
+
+    final element = library.extensionType('C');
+    assertInterfaceText(element, r'''
+map
+  foo: self::@extensionType::A::@method::foo
+  it: self::@extensionType::C::@getter::it
+declared
+  it: self::@extensionType::C::@getter::it
+overridden
+  foo
+    self::@extensionType::A::@method::foo
+  it
+    self::@extensionType::B1::@getter::it
+    self::@extensionType::B2::@getter::it
+''');
+  }
+
+  test_withObjectMembers() async {
+    final library = await buildLibrary(r'''
+extension type A(int it) {}
+''');
+
+    final element = library.extensionType('A');
+    printerConfiguration.withObjectMembers = true;
+    assertInterfaceText(element, r'''
+map
+  ==: dart:core::@class::Object::@method::==
+  hashCode: dart:core::@class::Object::@getter::hashCode
+  it: self::@extensionType::A::@getter::it
+  noSuchMethod: dart:core::@class::Object::@method::noSuchMethod
+  runtimeType: dart:core::@class::Object::@getter::runtimeType
+  toString: dart:core::@class::Object::@method::toString
+declared
+  it: self::@extensionType::A::@getter::it
+overridden
+  ==
+    dart:core::@class::Object::@method::==
+  hashCode
+    dart:core::@class::Object::@getter::hashCode
+  noSuchMethod
+    dart:core::@class::Object::@method::noSuchMethod
+  runtimeType
+    dart:core::@class::Object::@getter::runtimeType
+  toString
+    dart:core::@class::Object::@method::toString
+''');
+  }
+
+  String _interfaceText(InterfaceElementImpl element) {
+    final library = element.library;
+    final inheritance = library.session.inheritanceManager;
+    final interface = inheritance.getInterface(element);
+
+    final buffer = StringBuffer();
+    final sink = TreeStringSink(
+      sink: buffer,
+      indent: '',
+    );
+    final elementPrinter = ElementPrinter(
+      sink: sink,
+      configuration: ElementPrinterConfiguration(),
+      selfUriStr: '${library.source.uri}',
+    );
+
+    _InterfacePrinter(
+      sink: sink,
+      elementPrinter: elementPrinter,
+      configuration: printerConfiguration,
+    ).write(interface);
+
+    return buffer.toString();
+  }
+}
+
+@reflectiveTest
 class InheritanceManager3WithoutNullSafetyTest
     extends _InheritanceManager3Base {
   test_getInherited_closestSuper() async {
@@ -1626,3 +2213,140 @@
     expect(actual, expected);
   }
 }
+
+class _InstancePrinterConfiguration {
+  bool withObjectMembers = false;
+  bool withoutIdenticalImplemented = false;
+}
+
+class _InterfacePrinter {
+  final TreeStringSink _sink;
+  final ElementPrinter _elementPrinter;
+  final _InstancePrinterConfiguration _configuration;
+
+  _InterfacePrinter({
+    required TreeStringSink sink,
+    required ElementPrinter elementPrinter,
+    required _InstancePrinterConfiguration configuration,
+  })  : _sink = sink,
+        _elementPrinter = elementPrinter,
+        _configuration = configuration;
+
+  void write(Interface interface) {
+    _writeNameToMap('map', interface.map);
+    _writeNameToMap('declared', interface.declared);
+
+    if (_configuration.withoutIdenticalImplemented) {
+      expect(interface.implemented, same(interface.map));
+    } else {
+      _writeNameToMap('implemented', interface.implemented);
+    }
+
+    _writeNameToListMap('overridden', interface.overridden);
+    _writeListOfMaps('superImplemented', interface.superImplemented);
+    _writeNameToMap('inheritedMap', interface.inheritedMap ?? {});
+    _writeConflicts(interface.conflicts);
+  }
+
+  String _nameObjStr(Name nameObj) {
+    return nameObj.name;
+  }
+
+  bool _shouldWrite(ExecutableElement element) {
+    return _configuration.withObjectMembers || !element.isObjectMember;
+  }
+
+  List<MapEntry<Name, T>> _sortedEntries<T>(
+    Iterable<MapEntry<Name, T>> entries,
+  ) {
+    return entries.sortedBy(
+      (e) => '${e.key.name} ${e.key.libraryUri}',
+    );
+  }
+
+  List<ExecutableElement> _withoutObject(List<ExecutableElement> elements) {
+    return elements.where(_shouldWrite).toList();
+  }
+
+  void _writeConflicts(List<Conflict> conflicts) {
+    if (conflicts.isEmpty) return;
+
+    _sink.writelnWithIndent('conflicts');
+    _sink.withIndent(() {
+      for (final conflict in conflicts) {
+        switch (conflict) {
+          case CandidatesConflict _:
+            _elementPrinter.writeElementList(
+              'CandidatesConflict',
+              conflict.candidates,
+            );
+          case NotUniqueExtensionMemberConflict _:
+            _elementPrinter.writeElementList(
+              'NotUniqueExtensionMemberConflict',
+              conflict.candidates,
+            );
+          default:
+            fail('Not implemented: ${conflict.runtimeType}');
+        }
+      }
+    });
+  }
+
+  void _writeListOfMaps(
+    String name,
+    List<Map<Name, ExecutableElement>> listOfMaps,
+  ) {
+    if (listOfMaps.isEmpty) return;
+
+    _sink.writelnWithIndent(name);
+    _sink.withIndent(() {
+      listOfMaps.forEachIndexed((index, map) {
+        _writeNameToMap('$index', map);
+      });
+    });
+  }
+
+  void _writeNameToListMap(
+    String name,
+    Map<Name, List<ExecutableElement>> map,
+  ) {
+    final isEmpty = map.values.expand((elements) => elements).where((element) {
+      if (_configuration.withObjectMembers) return true;
+      return !element.isObjectMember;
+    }).isEmpty;
+    if (isEmpty) return;
+
+    _sink.writelnWithIndent(name);
+    _sink.withIndent(() {
+      for (final entry in _sortedEntries(map.entries)) {
+        final name = _nameObjStr(entry.key);
+        final elements = _withoutObject(entry.value);
+        _elementPrinter.writeElementList(name, elements);
+      }
+    });
+  }
+
+  void _writeNameToMap(String name, Map<Name, ExecutableElement> map) {
+    final isEmpty = map.values.none(_shouldWrite);
+    if (isEmpty) return;
+
+    _sink.writelnWithIndent(name);
+    _sink.withIndent(() {
+      for (final entry in _sortedEntries(map.entries)) {
+        final name = _nameObjStr(entry.key);
+        final element = entry.value;
+        if (_shouldWrite(element)) {
+          _elementPrinter.writeNamedElement(name, element);
+        }
+      }
+    });
+  }
+}
+
+extension on LibraryElementImpl {
+  ExtensionTypeElementImpl extensionType(String name) {
+    return topLevelElements
+        .whereType<ExtensionTypeElementImpl>()
+        .singleWhere((e) => e.name == name);
+  }
+}
diff --git a/pkg/analyzer/test/src/summary/elements_test.dart b/pkg/analyzer/test/src/summary/elements_test.dart
index 6d5a699..eb228c6 100644
--- a/pkg/analyzer/test/src/summary/elements_test.dart
+++ b/pkg/analyzer/test/src/summary/elements_test.dart
@@ -46508,6 +46508,8 @@
     extensionTypes
       A @21
         representation: self::@extensionType::A::@field::it
+        interfaces
+          Object
         fields
           final it @27
             type: int
@@ -46537,6 +46539,8 @@
         codeOffset: 0
         codeLength: 33
         representation: self::@extensionType::A::@field::it
+        interfaces
+          Object
         fields
           final it @27
             codeOffset: 23
@@ -46574,6 +46578,8 @@
         codeOffset: 0
         codeLength: 27
         representation: self::@extensionType::A::@field::it
+        interfaces
+          Object
         fields
           final it @21
             codeOffset: 17
@@ -46609,6 +46615,8 @@
     extensionTypes
       A @15
         representation: self::@extensionType::A::@field::it
+        interfaces
+          Object
         fields
           final it @21
             type: int
@@ -46641,6 +46649,8 @@
     extensionTypes
       A @15
         representation: self::@extensionType::A::@field::it
+        interfaces
+          Object
         fields
           final it @21
             type: int
@@ -46673,6 +46683,8 @@
     extensionTypes
       A @15
         representation: self::@extensionType::A::@field::it
+        interfaces
+          Object
         fields
           final it @21
             type: int
@@ -46705,6 +46717,8 @@
     extensionTypes
       A @32
         representation: self::@extensionType::A::@field::it
+        interfaces
+          Object
         fields
           final it @43
             metadata
@@ -46742,6 +46756,8 @@
     extensionTypes
       A @15
         representation: self::@extensionType::A::@field::it
+        interfaces
+          Object
         fields
           final it @21
             type: int
@@ -46802,6 +46818,8 @@
     extensionTypes
       A @15
         representation: self::@extensionType::A::@field::it
+        interfaces
+          Object
         fields
           final it @21
             type: num
@@ -46844,6 +46862,29 @@
 ''');
   }
 
+  test_interfaces_implicitObjectQuestion() async {
+    var library = await buildLibrary(r'''
+extension type X(int? it) {}
+''');
+
+    configuration.withConstructors = false;
+    checkElementText(library, r'''
+library
+  definingUnit
+    extensionTypes
+      X @15
+        representation: self::@extensionType::X::@field::it
+        interfaces
+          Object?
+        fields
+          final it @22
+            type: int?
+        accessors
+          synthetic get it @-1
+            returnType: int?
+''');
+  }
+
   test_interfaces_void() async {
     var library = await buildLibrary(r'''
 typedef A = void;
@@ -46898,6 +46939,8 @@
               staticType: null
             element: package:test/a.dart::@getter::foo
         representation: self::@extensionType::A::@field::it
+        interfaces
+          Object
         fields
           final it @43
             type: int
@@ -46927,6 +46970,8 @@
     extensionTypes
       A @15
         representation: self::@extensionType::A::@field::it
+        interfaces
+          Object
         fields
           final it @21
             type: int
@@ -46956,6 +47001,8 @@
     extensionTypes
       A @15
         representation: self::@extensionType::A::@field::it
+        interfaces
+          Object
         fields
           final it @21
             type: int
@@ -46989,6 +47036,8 @@
         codeOffset: 0
         codeLength: 21
         representation: self::@extensionType::A::@field::<empty>
+        interfaces
+          Object?
         fields
           final <empty> @17
             codeOffset: 17
@@ -47024,6 +47073,8 @@
     extensionTypes
       A @15
         representation: self::@extensionType::A::@field::it
+        interfaces
+          Object
         fields
           final it @21
             type: int
@@ -47055,6 +47106,8 @@
             bound: num
           covariant U @32
         representation: self::@extensionType::A::@field::it
+        interfaces
+          Object
         fields
           final it @45
             type: Map<T, U>