Fine. Track combined signatures, to support top-merges.

Change-Id: Ia8aad87209f304f03a993db7883eb7c988c2fdf0
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/424920
Reviewed-by: Paul Berry <paulberry@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 719e3f9..889bec1 100644
--- a/pkg/analyzer/lib/src/dart/element/inheritance_manager3.dart
+++ b/pkg/analyzer/lib/src/dart/element/inheritance_manager3.dart
@@ -80,6 +80,11 @@
   /// Cached instance interfaces for [InterfaceFragmentImpl].
   final Map<InterfaceFragmentImpl, Interface> _interfaces = {};
 
+  /// Tracks signatures from superinterfaces that were combined.
+  /// It is used to track dependencies in manifests.
+  final Map<InterfaceFragmentImpl, Map<Name, List<ExecutableElement2OrMember>>>
+  _combinedSignatures = {};
+
   /// The set of classes that are currently being processed, used to detect
   /// self-referencing cycles.
   final Set<InterfaceFragmentImpl> _processingClasses = {};
@@ -124,6 +129,9 @@
       return null;
     }
 
+    (_combinedSignatures[targetClass] ??= {})[name] =
+        candidates.map((e) => e.asElement2).toList();
+
     if (doTopMerge) {
       var typeSystem = targetClass.library.typeSystem;
       return _topMerge(typeSystem, targetClass, validOverrides);
@@ -786,6 +794,7 @@
       superImplemented: superImplemented,
       superImplemented2: superImplemented2,
       conflicts: conflicts.toFixedList(),
+      combinedSignatures: _combinedSignatures.remove(fragment) ?? {},
     );
   }
 
@@ -985,6 +994,7 @@
       superImplemented: const [],
       superImplemented2: const [],
       conflicts: conflicts.toFixedList(),
+      combinedSignatures: _combinedSignatures.remove(fragment) ?? {},
     );
   }
 
@@ -1057,6 +1067,7 @@
       superImplemented2: [superInterface2],
       conflicts:
           <Conflict>[...superConflicts, ...interfaceConflicts].toFixedList(),
+      combinedSignatures: _combinedSignatures.remove(fragment) ?? {},
     );
   }
 
@@ -1277,6 +1288,7 @@
     superImplemented: const [{}],
     superImplemented2: const [{}],
     conflicts: const [],
+    combinedSignatures: const {},
   );
 
   /// The map of names to their signature in the interface.
@@ -1327,6 +1339,10 @@
   /// members of the class.
   final List<Conflict> conflicts;
 
+  /// Tracks signatures from superinterfaces that were combined.
+  /// It is used to track dependencies in manifests.
+  final Map<Name, List<ExecutableElement2OrMember>> combinedSignatures;
+
   /// The map of names to the most specific signatures from the mixins,
   /// superclasses, or interfaces.
   Map<Name, ExecutableElementOrMember>? inheritedMap;
@@ -1344,6 +1360,7 @@
     required this.superImplemented,
     required this.superImplemented2,
     required this.conflicts,
+    required this.combinedSignatures,
   });
 
   /// The map of declared names to their signatures.
diff --git a/pkg/analyzer/lib/src/fine/library_manifest.dart b/pkg/analyzer/lib/src/fine/library_manifest.dart
index 519f149..a91c166 100644
--- a/pkg/analyzer/lib/src/fine/library_manifest.dart
+++ b/pkg/analyzer/lib/src/fine/library_manifest.dart
@@ -137,7 +137,6 @@
       typeParameters,
     ) {
       classItem.declaredMembers.clear();
-      classItem.interface.clear();
       _addInterfaceElementExecutables(
         encodingContext: encodingContext,
         instanceElement: element,
@@ -374,7 +373,6 @@
       typeParameters,
     ) {
       mixinItem.declaredMembers.clear();
-      mixinItem.interface.clear();
       _addInterfaceElementExecutables(
         encodingContext: encodingContext,
         instanceElement: element,
@@ -575,6 +573,7 @@
 
     // Must be created already.
     var item = declaredItems[element] as InterfaceItem;
+    item.interface.beforeUpdating();
 
     var interface = element.inheritanceManager.getInterface2(element);
     for (var entry in interface.map2.entries) {
@@ -591,9 +590,34 @@
         continue;
       }
 
+      var combinedCandidates = interface.combinedSignatures[entry.key];
+      if (combinedCandidates != null) {
+        var candidateElements =
+            combinedCandidates
+                .map((candidate) => candidate.baseElement)
+                .toSet()
+                .toList();
+        if (candidateElements.length == 1) {
+          executable = candidateElements[0];
+        } else {
+          var candidateIds =
+              candidateElements.map((candidate) {
+                return _getInterfaceElementMemberId(candidate);
+              }).toList();
+          var idList = ManifestItemIdList(candidateIds);
+          var id = item.interface.combinedIdsTemp[idList];
+          id ??= ManifestItemId.generate();
+          item.interface.map[lookupName] = id;
+          item.interface.combinedIds[idList] = id;
+          continue;
+        }
+      }
+
       var id = _getInterfaceElementMemberId(executable);
       item.interface.map[lookupName] = id;
     }
+
+    item.interface.afterUpdate();
   }
 
   void _fillInterfaceElementsInterface() {
diff --git a/pkg/analyzer/lib/src/fine/manifest_id.dart b/pkg/analyzer/lib/src/fine/manifest_id.dart
index d491fb9..30cca98 100644
--- a/pkg/analyzer/lib/src/fine/manifest_id.dart
+++ b/pkg/analyzer/lib/src/fine/manifest_id.dart
@@ -6,6 +6,7 @@
 
 import 'package:analyzer/src/summary2/data_reader.dart';
 import 'package:analyzer/src/summary2/data_writer.dart';
+import 'package:collection/collection.dart';
 
 /// The globally unique identifier.
 ///
@@ -51,11 +52,46 @@
     sink.writeUInt32(randomBits);
   }
 
+  static List<ManifestItemId> readList(SummaryDataReader reader) {
+    return reader.readTypedList(() => ManifestItemId.read(reader));
+  }
+
   static ManifestItemId? readOptional(SummaryDataReader reader) {
     return reader.readOptionalObject(() => ManifestItemId.read(reader));
   }
 }
 
+class ManifestItemIdList {
+  final List<ManifestItemId> ids;
+
+  ManifestItemIdList(this.ids);
+
+  factory ManifestItemIdList.read(SummaryDataReader reader) {
+    return ManifestItemIdList(ManifestItemId.readList(reader));
+  }
+
+  @override
+  int get hashCode {
+    return const ListEquality<ManifestItemId>().hash(ids);
+  }
+
+  @override
+  bool operator ==(Object other) {
+    if (identical(this, other)) return true;
+    return other is ManifestItemIdList &&
+        const ListEquality<ManifestItemId>().equals(other.ids, ids);
+  }
+
+  @override
+  String toString() {
+    return '[${ids.join(', ')}]';
+  }
+
+  void write(BufferedSink sink) {
+    sink.writeList(ids, (id) => id.write(sink));
+  }
+}
+
 extension ManifestItemIdExtension on ManifestItemId? {
   void writeOptional(BufferedSink sink) {
     sink.writeOptionalObject(this, (id) {
diff --git a/pkg/analyzer/lib/src/fine/manifest_item.dart b/pkg/analyzer/lib/src/fine/manifest_item.dart
index 7ea8a47..c5ca585 100644
--- a/pkg/analyzer/lib/src/fine/manifest_item.dart
+++ b/pkg/analyzer/lib/src/fine/manifest_item.dart
@@ -493,22 +493,48 @@
   /// The map of names to their IDs in the interface.
   final Map<LookupName, ManifestItemId> map;
 
-  ManifestInterface({required this.map});
+  /// Key: IDs of method declarations.
+  /// Value: ID assigned last time.
+  /// When the same signatures merge, the result is the same.
+  Map<ManifestItemIdList, ManifestItemId> combinedIds = {};
+
+  /// We move [combinedIds] into here during building the manifest, so that
+  /// we can fill [combinedIds] with new entries.
+  Map<ManifestItemIdList, ManifestItemId> combinedIdsTemp = {};
+
+  ManifestInterface({required this.map, required this.combinedIds});
 
   factory ManifestInterface.empty() {
-    return ManifestInterface(map: {});
+    return ManifestInterface(map: {}, combinedIds: {});
   }
 
   factory ManifestInterface.read(SummaryDataReader reader) {
-    return ManifestInterface(map: LookupNameIdMapExtension.read(reader));
+    return ManifestInterface(
+      map: LookupNameIdMapExtension.read(reader),
+      combinedIds: reader.readMap(
+        readKey: () => ManifestItemIdList.read(reader),
+        readValue: () => ManifestItemId.read(reader),
+      ),
+    );
   }
 
-  void clear() {
+  void afterUpdate() {
+    combinedIdsTemp = {};
+  }
+
+  void beforeUpdating() {
     map.clear();
+    combinedIdsTemp = combinedIds;
+    combinedIds = {};
   }
 
   void write(BufferedSink sink) {
     map.write(sink);
+    sink.writeMap(
+      combinedIds,
+      writeKey: (key) => key.write(sink),
+      writeValue: (id) => id.write(sink),
+    );
   }
 }
 
diff --git a/pkg/analyzer/test/src/dart/analysis/driver_test.dart b/pkg/analyzer/test/src/dart/analysis/driver_test.dart
index 4a226ec..ec203e3 100644
--- a/pkg/analyzer/test/src/dart/analysis/driver_test.dart
+++ b/pkg/analyzer/test/src/dart/analysis/driver_test.dart
@@ -25342,6 +25342,85 @@
     );
   }
 
+  test_manifest_class_getter_combinedSignatures_merged_addUnrelated() async {
+    await _runLibraryManifestScenario(
+      initialCode: r'''
+abstract class A {
+  num get foo;
+}
+
+abstract class B {
+  int get foo;
+}
+
+abstract class C implements A, B {}
+''',
+      expectedInitialEvents: r'''
+[operation] linkLibraryCycle SDK
+[operation] linkLibraryCycle
+  package:test/test.dart
+    manifest
+      A: #M0
+        declaredMembers
+          foo.getter: #M1
+        interface
+          map
+            foo: #M1
+      B: #M2
+        declaredMembers
+          foo.getter: #M3
+        interface
+          map
+            foo: #M3
+      C: #M4
+        interface
+          map
+            foo: #M5
+          combinedIds
+            [#M1, #M3]: #M5
+''',
+      updatedCode: r'''
+abstract class A {
+  num get foo;
+}
+
+abstract class B {
+  int get foo;
+}
+
+abstract class C implements A, B {
+  void zzz();
+}
+''',
+      expectedUpdatedEvents: r'''
+[operation] linkLibraryCycle
+  package:test/test.dart
+    manifest
+      A: #M0
+        declaredMembers
+          foo.getter: #M1
+        interface
+          map
+            foo: #M1
+      B: #M2
+        declaredMembers
+          foo.getter: #M3
+        interface
+          map
+            foo: #M3
+      C: #M4
+        declaredMembers
+          zzz.method: #M6
+        interface
+          map
+            foo: #M5
+            zzz: #M6
+          combinedIds
+            [#M1, #M3]: #M5
+''',
+    );
+  }
+
   test_manifest_class_getter_metadata() async {
     await _runLibraryManifestScenario(
       initialCode: r'''
@@ -26417,6 +26496,362 @@
     );
   }
 
+  test_manifest_class_method_combinedSignatures_conflict_removeOne() async {
+    await _runLibraryManifestScenario(
+      initialCode: r'''
+abstract class A {
+  int foo();
+}
+
+abstract class B {
+  double foo();
+}
+
+abstract class C implements A, B {}
+''',
+      expectedInitialEvents: r'''
+[operation] linkLibraryCycle SDK
+[operation] linkLibraryCycle
+  package:test/test.dart
+    manifest
+      A: #M0
+        declaredMembers
+          foo.method: #M1
+        interface
+          map
+            foo: #M1
+      B: #M2
+        declaredMembers
+          foo.method: #M3
+        interface
+          map
+            foo: #M3
+      C: #M4
+''',
+      updatedCode: r'''
+abstract class A {
+  int foo();
+}
+
+abstract class B {}
+
+abstract class C implements A, B {}
+''',
+      expectedUpdatedEvents: r'''
+[operation] linkLibraryCycle
+  package:test/test.dart
+    manifest
+      A: #M0
+        declaredMembers
+          foo.method: #M1
+        interface
+          map
+            foo: #M1
+      B: #M2
+      C: #M4
+        interface
+          map
+            foo: #M1
+''',
+    );
+  }
+
+  test_manifest_class_method_combinedSignatures_merged_addUnrelated() async {
+    await _runLibraryManifestScenario(
+      initialCode: r'''
+abstract class A {
+  dynamic foo();
+}
+
+abstract class B {
+  void foo();
+}
+
+abstract class C implements A, B {}
+''',
+      expectedInitialEvents: r'''
+[operation] linkLibraryCycle SDK
+[operation] linkLibraryCycle
+  package:test/test.dart
+    manifest
+      A: #M0
+        declaredMembers
+          foo.method: #M1
+        interface
+          map
+            foo: #M1
+      B: #M2
+        declaredMembers
+          foo.method: #M3
+        interface
+          map
+            foo: #M3
+      C: #M4
+        interface
+          map
+            foo: #M5
+          combinedIds
+            [#M1, #M3]: #M5
+''',
+      updatedCode: r'''
+abstract class A {
+  dynamic foo();
+}
+
+abstract class B {
+  void foo();
+  void zzz();
+}
+
+abstract class C implements A, B {}
+''',
+      expectedUpdatedEvents: r'''
+[operation] linkLibraryCycle
+  package:test/test.dart
+    manifest
+      A: #M0
+        declaredMembers
+          foo.method: #M1
+        interface
+          map
+            foo: #M1
+      B: #M2
+        declaredMembers
+          foo.method: #M3
+          zzz.method: #M6
+        interface
+          map
+            foo: #M3
+            zzz: #M6
+      C: #M4
+        interface
+          map
+            foo: #M5
+            zzz: #M6
+          combinedIds
+            [#M1, #M3]: #M5
+''',
+    );
+  }
+
+  test_manifest_class_method_combinedSignatures_merged_removeOne() async {
+    await _runLibraryManifestScenario(
+      initialCode: r'''
+abstract class A {
+  dynamic foo();
+}
+
+abstract class B {
+  void foo();
+}
+
+abstract class C implements A, B {}
+''',
+      expectedInitialEvents: r'''
+[operation] linkLibraryCycle SDK
+[operation] linkLibraryCycle
+  package:test/test.dart
+    manifest
+      A: #M0
+        declaredMembers
+          foo.method: #M1
+        interface
+          map
+            foo: #M1
+      B: #M2
+        declaredMembers
+          foo.method: #M3
+        interface
+          map
+            foo: #M3
+      C: #M4
+        interface
+          map
+            foo: #M5
+          combinedIds
+            [#M1, #M3]: #M5
+''',
+      updatedCode: r'''
+abstract class A {}
+
+abstract class B {
+  void foo();
+}
+
+abstract class C implements A, B {}
+''',
+      expectedUpdatedEvents: r'''
+[operation] linkLibraryCycle
+  package:test/test.dart
+    manifest
+      A: #M0
+      B: #M2
+        declaredMembers
+          foo.method: #M3
+        interface
+          map
+            foo: #M3
+      C: #M4
+        interface
+          map
+            foo: #M3
+''',
+    );
+  }
+
+  test_manifest_class_method_combinedSignatures_merged_sameBase() async {
+    await _runLibraryManifestScenario(
+      initialCode: r'''
+abstract class A<T> {
+  T foo();
+}
+
+abstract class B implements A<dynamic> {}
+
+abstract class C implements A<void> {}
+
+abstract class D implements B, C {}
+''',
+      expectedInitialEvents: r'''
+[operation] linkLibraryCycle SDK
+[operation] linkLibraryCycle
+  package:test/test.dart
+    manifest
+      A: #M0
+        declaredMembers
+          foo.method: #M1
+        interface
+          map
+            foo: #M1
+      B: #M2
+        interface
+          map
+            foo: #M1
+      C: #M3
+        interface
+          map
+            foo: #M1
+      D: #M4
+        interface
+          map
+            foo: #M1
+''',
+      updatedCode: r'''
+abstract class A<T> {
+  T foo();
+}
+
+abstract class B implements A<dynamic> {}
+
+abstract class C implements A<void> {}
+
+abstract class D implements B, C {
+  void zzz() {}
+}
+''',
+      expectedUpdatedEvents: r'''
+[operation] linkLibraryCycle
+  package:test/test.dart
+    manifest
+      A: #M0
+        declaredMembers
+          foo.method: #M1
+        interface
+          map
+            foo: #M1
+      B: #M2
+        interface
+          map
+            foo: #M1
+      C: #M3
+        interface
+          map
+            foo: #M1
+      D: #M4
+        declaredMembers
+          zzz.method: #M5
+        interface
+          map
+            foo: #M1
+            zzz: #M5
+''',
+    );
+  }
+
+  test_manifest_class_method_combinedSignatures_merged_updateOne() async {
+    await _runLibraryManifestScenario(
+      initialCode: r'''
+abstract class A {
+  dynamic foo();
+}
+
+abstract class B {
+  void foo();
+}
+
+abstract class C implements A, B {}
+''',
+      expectedInitialEvents: r'''
+[operation] linkLibraryCycle SDK
+[operation] linkLibraryCycle
+  package:test/test.dart
+    manifest
+      A: #M0
+        declaredMembers
+          foo.method: #M1
+        interface
+          map
+            foo: #M1
+      B: #M2
+        declaredMembers
+          foo.method: #M3
+        interface
+          map
+            foo: #M3
+      C: #M4
+        interface
+          map
+            foo: #M5
+          combinedIds
+            [#M1, #M3]: #M5
+''',
+      updatedCode: r'''
+abstract class A {
+  dynamic foo();
+}
+
+abstract class B {
+  int foo();
+}
+
+abstract class C implements A, B {}
+''',
+      expectedUpdatedEvents: r'''
+[operation] linkLibraryCycle
+  package:test/test.dart
+    manifest
+      A: #M0
+        declaredMembers
+          foo.method: #M1
+        interface
+          map
+            foo: #M1
+      B: #M2
+        declaredMembers
+          foo.method: #M6
+        interface
+          map
+            foo: #M6
+      C: #M4
+        interface
+          map
+            foo: #M7
+          combinedIds
+            [#M1, #M6]: #M7
+''',
+    );
+  }
+
   test_manifest_class_method_formalParameter_optionalNamed() async {
     await _runLibraryManifestScenario(
       initialCode: r'''
@@ -28150,6 +28585,85 @@
     );
   }
 
+  test_manifest_class_setter_combinedSignatures_merged_addUnrelated() async {
+    await _runLibraryManifestScenario(
+      initialCode: r'''
+abstract class A {
+  set foo(num _);
+}
+
+abstract class B {
+  set foo(int _);
+}
+
+abstract class C implements A, B {}
+''',
+      expectedInitialEvents: r'''
+[operation] linkLibraryCycle SDK
+[operation] linkLibraryCycle
+  package:test/test.dart
+    manifest
+      A: #M0
+        declaredMembers
+          foo.setter: #M1
+        interface
+          map
+            foo=: #M1
+      B: #M2
+        declaredMembers
+          foo.setter: #M3
+        interface
+          map
+            foo=: #M3
+      C: #M4
+        interface
+          map
+            foo=: #M5
+          combinedIds
+            [#M1, #M3]: #M5
+''',
+      updatedCode: r'''
+abstract class A {
+  set foo(num _);
+}
+
+abstract class B {
+  set foo(int _);
+}
+
+abstract class C implements A, B {
+  void zzz();
+}
+''',
+      expectedUpdatedEvents: r'''
+[operation] linkLibraryCycle
+  package:test/test.dart
+    manifest
+      A: #M0
+        declaredMembers
+          foo.setter: #M1
+        interface
+          map
+            foo=: #M1
+      B: #M2
+        declaredMembers
+          foo.setter: #M3
+        interface
+          map
+            foo=: #M3
+      C: #M4
+        declaredMembers
+          zzz.method: #M6
+        interface
+          map
+            foo=: #M5
+            zzz: #M6
+          combinedIds
+            [#M1, #M3]: #M5
+''',
+    );
+  }
+
   test_manifest_class_setter_metadata() async {
     await _runLibraryManifestScenario(
       initialCode: r'''
@@ -28630,6 +29144,101 @@
     );
   }
 
+  test_manifest_class_setter_topMerge() async {
+    configuration.withElementManifests = true;
+    await _runLibraryManifestScenario(
+      initialCode: r'''
+abstract class A {
+  set foo(List _);
+}
+
+abstract class B {
+  set foo(List<void> _);
+}
+
+abstract class C implements A, B {}
+''',
+      expectedInitialEvents: r'''
+[operation] linkLibraryCycle SDK
+[operation] linkLibraryCycle
+  package:test/test.dart
+    manifest
+      A: #M0
+        supertype: Object @ dart:core
+        declaredMembers
+          foo.setter: #M1
+            valueType: List @ dart:core
+              dynamic
+        interface
+          map
+            foo=: #M1
+      B: #M2
+        supertype: Object @ dart:core
+        declaredMembers
+          foo.setter: #M3
+            valueType: List @ dart:core
+              void
+        interface
+          map
+            foo=: #M3
+      C: #M4
+        supertype: Object @ dart:core
+        interfaces
+          A @ package:test/test.dart
+          B @ package:test/test.dart
+        interface
+          map
+            foo=: #M5
+          combinedIds
+            [#M1, #M3]: #M5
+''',
+      updatedCode: r'''
+abstract class A {
+  set foo(List _);
+}
+
+abstract class B {
+  set foo(List<int> _);
+}
+
+abstract class C implements A, B {}
+''',
+      expectedUpdatedEvents: r'''
+[operation] linkLibraryCycle
+  package:test/test.dart
+    manifest
+      A: #M0
+        supertype: Object @ dart:core
+        declaredMembers
+          foo.setter: #M1
+            valueType: List @ dart:core
+              dynamic
+        interface
+          map
+            foo=: #M1
+      B: #M2
+        supertype: Object @ dart:core
+        declaredMembers
+          foo.setter: #M6
+            valueType: List @ dart:core
+              int @ dart:core
+        interface
+          map
+            foo=: #M6
+      C: #M4
+        supertype: Object @ dart:core
+        interfaces
+          A @ package:test/test.dart
+          B @ package:test/test.dart
+        interface
+          map
+            foo=: #M7
+          combinedIds
+            [#M1, #M6]: #M7
+''',
+    );
+  }
+
   test_manifest_class_setter_valueType() async {
     configuration.withElementManifests = true;
     await _runLibraryManifestScenario(
diff --git a/pkg/analyzer/test/src/dart/analysis/result_printer.dart b/pkg/analyzer/test/src/dart/analysis/result_printer.dart
index c1153bb..22d6913 100644
--- a/pkg/analyzer/test/src/dart/analysis/result_printer.dart
+++ b/pkg/analyzer/test/src/dart/analysis/result_printer.dart
@@ -935,6 +935,20 @@
             }
           });
         }
+
+        var combinedIds = item.interface.combinedIds;
+        if (combinedIds.isNotEmpty) {
+          sink.writelnWithIndent('combinedIds');
+          sink.withIndent(() {
+            for (var entry in combinedIds.entries) {
+              var idListStr = entry.key.ids
+                  .map((id) => idProvider.manifestId(id))
+                  .join(', ');
+              var idStr = idProvider.manifestId(entry.value);
+              sink.writelnWithIndent('[$idListStr]: $idStr');
+            }
+          });
+        }
       });
     }
   }