Extension types. Update precluded executables.

See https://github.com/dart-lang/language/commit/8cb002ff08f79abb433fb9c0311f58a20398aceb

Change-Id: I627977abbfe8f8620c76b9a9dff0643c8f35f67f
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/339229
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
diff --git a/pkg/analyzer/lib/src/dart/element/inheritance_manager3.dart b/pkg/analyzer/lib/src/dart/element/inheritance_manager3.dart
index 72c5bd5..d7f1678 100644
--- a/pkg/analyzer/lib/src/dart/element/inheritance_manager3.dart
+++ b/pkg/analyzer/lib/src/dart/element/inheritance_manager3.dart
@@ -708,17 +708,19 @@
     final implemented = Map.of(declared);
 
     // Prepare candidates for inheritance.
-    final extensionCandidates = <Name, List<ExecutableElement>>{};
-    final notExtensionCandidates = <Name, List<ExecutableElement>>{};
+    final extensionCandidates = <Name, _ExtensionTypeCandidates>{};
+    final notExtensionCandidates = <Name, _ExtensionTypeCandidates>{};
     for (final interface in augmented.interfaces) {
       final substitution = Substitution.fromInterfaceType(interface);
       for (final entry in getInterface(interface.element).map.entries) {
         final name = entry.key;
         final executable = ExecutableMember.from2(entry.value, substitution);
         if (executable.isExtensionTypeMember) {
-          (extensionCandidates[name] ??= []).add(executable);
+          (extensionCandidates[name] ??= _ExtensionTypeCandidates(name))
+              .add(executable);
         } else {
-          (notExtensionCandidates[name] ??= []).add(executable);
+          (notExtensionCandidates[name] ??= _ExtensionTypeCandidates(name))
+              .add(executable);
         }
       }
     }
@@ -730,29 +732,42 @@
     for (final entry in extensionCandidates.entries) {
       final name = entry.key;
       final candidates = entry.value;
-      (redeclared[name] ??= []).addAll(candidates);
+      (redeclared[name] ??= []).addAll(candidates.all);
 
-      // Stop if precluded.
-      if (precludedNames.contains(name)) {
+      final notPrecluded = candidates.notPrecluded(
+        precludedNames: precludedNames,
+        precludedMethods: precludedMethods,
+        precludedSetters: precludedSetters,
+      );
+
+      // Stop if all precluded.
+      if (notPrecluded.isEmpty) {
         continue;
       }
 
-      // If not redeclared, can have either non-extension, or extension.
+      // If not precluded, can have either non-extension, or extension.
       final nonExtensionSignatures = notExtensionCandidates[name];
       if (nonExtensionSignatures != null) {
-        conflicts.add(
-          HasNonExtensionAndExtensionMemberConflict(
-            name: name,
-            nonExtension: nonExtensionSignatures,
-            extension: candidates,
-          ),
+        final notExtensionNotPrecluded = nonExtensionSignatures.notPrecluded(
+          precludedNames: precludedNames,
+          precludedMethods: precludedMethods,
+          precludedSetters: precludedSetters,
         );
+        if (notExtensionNotPrecluded.isNotEmpty) {
+          conflicts.add(
+            HasNonExtensionAndExtensionMemberConflict(
+              name: name,
+              nonExtension: notExtensionNotPrecluded,
+              extension: notPrecluded,
+            ),
+          );
+        }
         continue;
       }
 
       // The inherited member must be unique.
       ExecutableElement? uniqueElement;
-      for (final candidate in candidates) {
+      for (final candidate in notPrecluded) {
         if (uniqueElement == null) {
           uniqueElement = candidate;
         } else if (uniqueElement.declaration != candidate.declaration) {
@@ -765,24 +780,12 @@
         conflicts.add(
           NotUniqueExtensionMemberConflict(
             name: name,
-            candidates: candidates,
+            candidates: notPrecluded,
           ),
         );
         continue;
       }
 
-      // Stop if precluded.
-      switch (uniqueElement) {
-        case MethodElement():
-          if (precludedMethods.contains(name)) {
-            continue;
-          }
-        case PropertyAccessorElement(isSetter: true):
-          if (precludedSetters.contains(name)) {
-            continue;
-          }
-      }
-
       implemented[name] = uniqueElement;
     }
 
@@ -790,10 +793,16 @@
     for (final entry in notExtensionCandidates.entries) {
       final name = entry.key;
       final candidates = entry.value;
-      (redeclared[name] ??= []).addAll(candidates);
+      (redeclared[name] ??= []).addAll(candidates.all);
 
-      // Stop if precluded.
-      if (precludedNames.contains(name)) {
+      final notPrecluded = candidates.notPrecluded(
+        precludedNames: precludedNames,
+        precludedMethods: precludedMethods,
+        precludedSetters: precludedSetters,
+      );
+
+      // Stop if all precluded.
+      if (notPrecluded.isEmpty) {
         continue;
       }
 
@@ -805,7 +814,7 @@
 
       final combinedSignature = combineSignatures(
         targetClass: element,
-        candidates: candidates,
+        candidates: notPrecluded,
         doTopMerge: true,
         name: name,
       );
@@ -814,24 +823,12 @@
         conflicts.add(
           CandidatesConflict(
             name: name,
-            candidates: candidates,
+            candidates: notPrecluded,
           ),
         );
         continue;
       }
 
-      // Stop if precluded.
-      switch (combinedSignature) {
-        case MethodElement():
-          if (precludedMethods.contains(name)) {
-            continue;
-          }
-        case PropertyAccessorElement(isSetter: true):
-          if (precludedSetters.contains(name)) {
-            continue;
-          }
-      }
-
       implemented[name] = combinedSignature;
     }
 
@@ -1262,6 +1259,45 @@
   });
 }
 
+class _ExtensionTypeCandidates {
+  final Name name;
+  final List<MethodElement> methods = [];
+  final List<PropertyAccessorElement> getters = [];
+  final List<PropertyAccessorElement> setters = [];
+
+  _ExtensionTypeCandidates(this.name);
+
+  List<ExecutableElement> get all {
+    return [...methods, ...getters, ...setters];
+  }
+
+  void add(ExecutableElement element) {
+    switch (element) {
+      case MethodElement():
+        methods.add(element);
+      case PropertyAccessorElement(isGetter: true):
+        getters.add(element);
+      case PropertyAccessorElement(isSetter: true):
+        setters.add(element);
+    }
+  }
+
+  List<ExecutableElement> notPrecluded({
+    required Set<Name> precludedNames,
+    required Set<Name> precludedMethods,
+    required Set<Name> precludedSetters,
+  }) {
+    if (precludedNames.contains(name)) {
+      return const [];
+    }
+    return [
+      if (!precludedMethods.contains(name)) ...methods,
+      ...getters,
+      if (!precludedSetters.contains(name)) ...setters,
+    ];
+  }
+}
+
 class _ParameterDesc {
   final int? index;
   final String? name;
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 a407216..35953aa 100644
--- a/pkg/analyzer/test/src/dart/element/inheritance_manager3_test.dart
+++ b/pkg/analyzer/test/src/dart/element/inheritance_manager3_test.dart
@@ -1080,7 +1080,7 @@
 ''');
   }
 
-  test_noDeclaration_implementClass_implementExtensionType_hasConflict() async {
+  test_noDeclaration_implementClass_implementExtensionType_hasConflict_methods() async {
     final library = await buildLibrary(r'''
 class A {
   void foo() {}
@@ -1109,7 +1109,7 @@
   foo: self::@extensionType::B::@method::foo
   it: self::@extensionType::B::@getter::it
 conflicts
-  HasExtensionAndNotExtensionMemberConflict
+  HasNonExtensionAndExtensionMemberConflict
     nonExtension
       self::@class::A::@method::foo
     extension
@@ -1117,6 +1117,113 @@
 ''');
   }
 
+  test_noDeclaration_implementClass_implementExtensionType_hasConflict_setters() async {
+    final library = await buildLibrary(r'''
+class A {
+  set foo(int _) {}
+}
+
+extension type B(A it) {
+  set foo(int _) {}
+}
+
+extension type C(A 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
+redeclared
+  foo=
+    self::@extensionType::B::@setter::foo
+    self::@class::A::@setter::foo
+  it
+    self::@extensionType::B::@getter::it
+inheritedMap
+  foo=: self::@extensionType::B::@setter::foo
+  it: self::@extensionType::B::@getter::it
+conflicts
+  HasNonExtensionAndExtensionMemberConflict
+    nonExtension
+      self::@class::A::@setter::foo
+    extension
+      self::@extensionType::B::@setter::foo
+''');
+  }
+
+  test_noDeclaration_implementClass_implementExtensionType_noConflict_methodPrecludesSetters() async {
+    final library = await buildLibrary(r'''
+class A {
+  set foo(int _) {}
+}
+
+extension type B(A it) {
+  set foo(int _) {}
+}
+
+extension type C(A 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
+redeclared
+  foo=
+    self::@extensionType::B::@setter::foo
+    self::@class::A::@setter::foo
+  it
+    self::@extensionType::B::@getter::it
+inheritedMap
+  foo=: self::@extensionType::B::@setter::foo
+  it: self::@extensionType::B::@getter::it
+''');
+  }
+
+  test_noDeclaration_implementClass_implementExtensionType_noConflict_setterPrecludesMethods() async {
+    final library = await buildLibrary(r'''
+class A {
+  void foo() {}
+}
+
+extension type B(A it) {
+  void foo() {}
+}
+
+extension type C(A it) implements A, B {
+  set foo(int _) {}
+}
+''');
+
+    final element = library.extensionType('C');
+    assertInterfaceText(element, r'''
+map
+  foo=: self::@extensionType::C::@setter::foo
+  it: self::@extensionType::C::@getter::it
+declared
+  foo=: self::@extensionType::C::@setter::foo
+  it: self::@extensionType::C::@getter::it
+redeclared
+  foo
+    self::@extensionType::B::@method::foo
+    self::@class::A::@method::foo
+  it
+    self::@extensionType::B::@getter::it
+inheritedMap
+  foo: self::@extensionType::B::@method::foo
+  it: self::@extensionType::B::@getter::it
+''');
+  }
+
   test_noDeclaration_implementClass_method() async {
     final library = await buildLibrary(r'''
 class A {
@@ -1358,7 +1465,43 @@
 ''');
   }
 
-  test_noDeclaration_implementExtensionType_method2_noConflict() async {
+  test_noDeclaration_implementExtensionType_method2_noConflict_setterPrecludes() 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 {
+  set foo(int _) {}
+}
+''');
+
+    final element = library.extensionType('B');
+    assertInterfaceText(element, r'''
+map
+  foo=: self::@extensionType::B::@setter::foo
+  it: self::@extensionType::B::@getter::it
+declared
+  foo=: self::@extensionType::B::@setter::foo
+  it: self::@extensionType::B::@getter::it
+redeclared
+  foo
+    self::@extensionType::A1::@method::foo
+    self::@extensionType::A2::@method::foo
+  it
+    self::@extensionType::A1::@getter::it
+    self::@extensionType::A2::@getter::it
+inheritedMap
+  foo: self::@extensionType::A1::@method::foo
+  it: self::@extensionType::A1::@getter::it
+''');
+  }
+
+  test_noDeclaration_implementExtensionType_method2_noConflict_unique() async {
     final library = await buildLibrary(r'''
 extension type A(int it) {
   void foo() {}
@@ -1390,6 +1533,110 @@
 ''');
   }
 
+  test_noDeclaration_implementExtensionType_setter2_hasConflict() async {
+    final library = await buildLibrary(r'''
+extension type A1(int it) {
+  set foo(int _) {}
+}
+
+extension type A2(int it) {
+  set foo(int _) {}
+}
+
+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
+redeclared
+  foo=
+    self::@extensionType::A1::@setter::foo
+    self::@extensionType::A2::@setter::foo
+  it
+    self::@extensionType::A1::@getter::it
+    self::@extensionType::A2::@getter::it
+inheritedMap
+  foo=: self::@extensionType::A1::@setter::foo
+  it: self::@extensionType::A1::@getter::it
+conflicts
+  NotUniqueExtensionMemberConflict
+    self::@extensionType::A1::@setter::foo
+    self::@extensionType::A2::@setter::foo
+''');
+  }
+
+  test_noDeclaration_implementExtensionType_setter2_noConflict_methodPrecludes() async {
+    final library = await buildLibrary(r'''
+extension type A1(int it) {
+  set foo(int _) {}
+}
+
+extension type A2(int it) {
+  set foo(int _) {}
+}
+
+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
+redeclared
+  foo=
+    self::@extensionType::A1::@setter::foo
+    self::@extensionType::A2::@setter::foo
+  it
+    self::@extensionType::A1::@getter::it
+    self::@extensionType::A2::@getter::it
+inheritedMap
+  foo=: self::@extensionType::A1::@setter::foo
+  it: self::@extensionType::A1::@getter::it
+''');
+  }
+
+  test_noDeclaration_implementExtensionType_setter2_noConflict_unique() async {
+    final library = await buildLibrary(r'''
+extension type A(int it) {
+  set foo(int _) {}
+}
+
+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::@setter::foo
+  it: self::@extensionType::C::@getter::it
+declared
+  it: self::@extensionType::C::@getter::it
+redeclared
+  foo=
+    self::@extensionType::A::@setter::foo
+  it
+    self::@extensionType::B1::@getter::it
+    self::@extensionType::B2::@getter::it
+inheritedMap
+  foo=: self::@extensionType::A::@setter::foo
+  it: self::@extensionType::B1::@getter::it
+''');
+  }
+
   test_withObjectMembers() async {
     final library = await buildLibrary(r'''
 extension type A(int it) {}
@@ -2733,7 +2980,7 @@
             );
           case HasNonExtensionAndExtensionMemberConflict _:
             _sink.writelnWithIndent(
-              'HasExtensionAndNotExtensionMemberConflict',
+              'HasNonExtensionAndExtensionMemberConflict',
             );
             _sink.withIndent(() {
               _elementPrinter.writeElementList(