Collect abstract members immediately instead of counting.

This simplifies diagnosing missing implementations, especially, the
ones inherited from interfaces.

Change-Id: I13705eee07960974a111d78f1eb986e77d27a468
Reviewed-on: https://dart-review.googlesource.com/c/88719
Reviewed-by: Dmitry Stefantsov <dmitryas@google.com>
diff --git a/pkg/front_end/lib/src/fasta/kernel/class_hierarchy_builder.dart b/pkg/front_end/lib/src/fasta/kernel/class_hierarchy_builder.dart
index a6074c2..a6c5dc6 100644
--- a/pkg/front_end/lib/src/fasta/kernel/class_hierarchy_builder.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/class_hierarchy_builder.dart
@@ -71,7 +71,7 @@
 
   bool hasNoSuchMethod = false;
 
-  int abstractMemberCount = 0;
+  List<Declaration> abstractMembers = null;
 
   ClassHierarchyBuilder(this.objectClass);
 
@@ -131,7 +131,7 @@
         // superclass.
         result = b;
       } else {
-        abstractMemberCount++;
+        (abstractMembers ??= <Declaration>[]).add(a);
       }
     }
     return result;
@@ -146,9 +146,8 @@
   /// implementing/overriding anything.
   void handleOnlyA(Declaration member, MergeKind mergeKind) {
     Member target = member.target;
-    if (mergeKind == MergeKind.supertypes ||
-        (mergeKind == MergeKind.superclass && target.isAbstract)) {
-      abstractMemberCount++;
+    if (mergeKind == MergeKind.superclass && target.isAbstract) {
+      (abstractMembers ??= <Declaration>[]).add(member);
     }
   }
 
@@ -165,7 +164,7 @@
     if (mergeKind == MergeKind.supertypes ||
         (mergeKind == MergeKind.superclass && target.isAbstract)) {
       if (isNameVisibleIn(target.name, cls.library)) {
-        abstractMemberCount++;
+        (abstractMembers ??= <Declaration>[]).add(member);
       }
     }
     if (member.parent != objectClass &&
@@ -177,7 +176,7 @@
 
   void add(KernelClassBuilder cls) {
     assert(!hasNoSuchMethod);
-    assert(abstractMemberCount == 0);
+    assert(abstractMembers == null);
     if (cls.isPatch) {
       // TODO(ahe): What about patch classes. Have we injected patched members
       // into the class-builder's scope?
@@ -203,6 +202,10 @@
         scope = mixin.scope;
       }
     }
+    // TODO(ahe): Consider if removing static members from [localMembers] and
+    // [localSetters] makes sense. It depends on what semantic checks we need
+    // to perform with respect to static members and inherited members with the
+    // same name.
     List<Declaration> localMembers =
         new List<Declaration>.from(scope.local.values)
           ..sort(compareDeclarations);
@@ -244,14 +247,15 @@
     nodes[cls] = new ClassHierarchyNode(cls, scope, classMembers, classSetters,
         interfaceMembers, interfaceSetters);
 
-    if (abstractMemberCount != 0 && !cls.isAbstract) {
+    if (abstractMembers != null && !cls.isAbstract) {
       if (!hasNoSuchMethod) {
-        reportMissingMembers(cls, classMembers, classSetters);
+        reportMissingMembers(cls);
+      } else {
+        installNsmHandlers(cls);
       }
-      installNsmHandlers(cls);
     }
     hasNoSuchMethod = false;
-    abstractMemberCount = 0;
+    abstractMembers = null;
   }
 
   MergeResult mergeInterfaces(KernelClassBuilder cls,
@@ -367,32 +371,31 @@
     }
   }
 
-  void reportMissingMembers(KernelClassBuilder cls, List<Declaration> members,
-      List<Declaration> setters) {
-    List<LocatedMessage> context = <LocatedMessage>[];
-    List<String> missingNames = <String>[];
-    for (int j = 0; j < 2; j++) {
-      List<Declaration> declarations = j == 0 ? members : setters;
-      for (int i = 0; i < declarations.length; i++) {
-        Declaration declaration = declarations[i];
-        Member target = declaration.target;
-        if (target.isAbstract && isNameVisibleIn(target.name, cls.library)) {
-          String name = declaration.fullNameForErrors;
-          String parentName = declaration.parent.fullNameForErrors;
-          String displayName =
-              declaration.isSetter ? "$parentName.$name=" : "$parentName.$name";
-          missingNames.add(displayName);
-          context.add(templateMissingImplementationCause
-              .withArguments(displayName)
-              .withLocation(
-                  declaration.fileUri, declaration.charOffset, name.length));
-        }
+  void reportMissingMembers(KernelClassBuilder cls) {
+    Map<String, LocatedMessage> contextMap = <String, LocatedMessage>{};
+    for (int i = 0; i < abstractMembers.length; i++) {
+      Declaration declaration = abstractMembers[i];
+      Member target = declaration.target;
+      if (isNameVisibleIn(target.name, cls.library)) {
+        String name = declaration.fullNameForErrors;
+        String parentName = declaration.parent.fullNameForErrors;
+        String displayName =
+            declaration.isSetter ? "$parentName.$name=" : "$parentName.$name";
+        contextMap[displayName] = templateMissingImplementationCause
+            .withArguments(displayName)
+            .withLocation(
+                declaration.fileUri, declaration.charOffset, name.length);
       }
     }
-    if (missingNames.isEmpty) return;
+    if (contextMap.isEmpty) return;
+    List<String> names = new List<String>.from(contextMap.keys)..sort();
+    List<LocatedMessage> context = <LocatedMessage>[];
+    for (int i = 0; i < names.length; i++) {
+      context.add(contextMap[names[i]]);
+    }
     cls.addProblem(
         templateMissingImplementationNotAbstract.withArguments(
-            cls.fullNameForErrors, missingNames),
+            cls.fullNameForErrors, names),
         cls.charOffset,
         cls.fullNameForErrors.length,
         context: context);
@@ -409,11 +412,11 @@
       if (node == null) {
         bool savedHasNoSuchMethod = hasNoSuchMethod;
         hasNoSuchMethod = false;
-        int savedAbstractMemberCount = abstractMemberCount;
-        abstractMemberCount = 0;
+        List<Declaration> savedAbstractMembers = abstractMembers;
+        abstractMembers = null;
         add(declaration);
         hasNoSuchMethod = savedHasNoSuchMethod;
-        abstractMemberCount = savedAbstractMemberCount;
+        abstractMembers = savedAbstractMembers;
         node = nodes[declaration];
       }
       return node;
@@ -430,14 +433,20 @@
     final List<Declaration> result = new List<Declaration>.filled(
         aList.length + bList.length, null,
         growable: true);
-
     int storeIndex = 0;
-
     int i = 0;
     int j = 0;
     while (i < aList.length && j < bList.length) {
       final Declaration a = aList[i];
       final Declaration b = bList[j];
+      if (a.isStatic) {
+        i++;
+        continue;
+      }
+      if (b.isStatic) {
+        j++;
+        continue;
+      }
       final int compare = compareDeclarations(a, b);
       if (compare == 0) {
         result[storeIndex++] = handleMergeConflict(cls, a, b, mergeKind);
@@ -455,14 +464,18 @@
     }
     while (i < aList.length) {
       final Declaration a = aList[i];
-      handleOnlyA(a, mergeKind);
-      result[storeIndex++] = a;
+      if (!a.isStatic) {
+        handleOnlyA(a, mergeKind);
+        result[storeIndex++] = a;
+      }
       i++;
     }
     while (j < bList.length) {
       final Declaration b = bList[j];
-      handleOnlyB(cls, b, mergeKind);
-      result[storeIndex++] = b;
+      if (!b.isStatic) {
+        handleOnlyB(cls, b, mergeKind);
+        result[storeIndex++] = b;
+      }
       j++;
     }
     return result..length = storeIndex;
diff --git a/pkg/front_end/testcases/abstract_members.dart.legacy.expect b/pkg/front_end/testcases/abstract_members.dart.legacy.expect
index 3ac995d..ae7e4d9 100644
--- a/pkg/front_end/testcases/abstract_members.dart.legacy.expect
+++ b/pkg/front_end/testcases/abstract_members.dart.legacy.expect
@@ -12,8 +12,12 @@
 //
 // pkg/front_end/testcases/abstract_members.dart:33:7: Error: The non-abstract class 'MyClass' is missing implementations for these members:
 //  - A.abstractMethod
-//  - A.property3=
 //  - A.property1=
+//  - A.property3=
+//  - Interface1.interfaceMethod1
+//  - Interface2.interfaceMethod1
+//  - Interface2.interfaceMethod2
+//  - Interface3.interfaceMethod3
 // Try to either
 //  - provide an implementation,
 //  - inherit an implementation from a superclass or mixin,
@@ -25,12 +29,24 @@
 // pkg/front_end/testcases/abstract_members.dart:21:3: Context: 'A.abstractMethod' is defined here.
 //   abstractMethod();
 //   ^^^^^^^^^^^^^^
-// pkg/front_end/testcases/abstract_members.dart:24:12: Context: 'A.property3=' is defined here.
-//   void set property3(_);
-//            ^^^^^^^^^
 // pkg/front_end/testcases/abstract_members.dart:22:12: Context: 'A.property1=' is defined here.
 //   void set property1(_);
 //            ^^^^^^^^^
+// pkg/front_end/testcases/abstract_members.dart:24:12: Context: 'A.property3=' is defined here.
+//   void set property3(_);
+//            ^^^^^^^^^
+// pkg/front_end/testcases/abstract_members.dart:6:8: Context: 'Interface1.interfaceMethod1' is defined here.
+//   void interfaceMethod1() {}
+//        ^^^^^^^^^^^^^^^^
+// pkg/front_end/testcases/abstract_members.dart:12:7: Context: 'Interface2.interfaceMethod1' is defined here.
+//   var interfaceMethod1;
+//       ^^^^^^^^^^^^^^^^
+// pkg/front_end/testcases/abstract_members.dart:10:8: Context: 'Interface2.interfaceMethod2' is defined here.
+//   void interfaceMethod2() {}
+//        ^^^^^^^^^^^^^^^^
+// pkg/front_end/testcases/abstract_members.dart:16:8: Context: 'Interface3.interfaceMethod3' is defined here.
+//   void interfaceMethod3() {}
+//        ^^^^^^^^^^^^^^^^
 
 // Unhandled errors:
 //
@@ -40,8 +56,12 @@
 //
 // pkg/front_end/testcases/abstract_members.dart:33:7: Error: The non-abstract class 'MyClass' is missing implementations for these members:
 //  - A.abstractMethod
-//  - A.property3=
 //  - A.property1=
+//  - A.property3=
+//  - Interface1.interfaceMethod1
+//  - Interface2.interfaceMethod1
+//  - Interface2.interfaceMethod2
+//  - Interface3.interfaceMethod3
 // Try to either
 //  - provide an implementation,
 //  - inherit an implementation from a superclass or mixin,
diff --git a/pkg/front_end/testcases/abstract_members.dart.legacy.transformed.expect b/pkg/front_end/testcases/abstract_members.dart.legacy.transformed.expect
index 1e1ebac..932cfc2 100644
--- a/pkg/front_end/testcases/abstract_members.dart.legacy.transformed.expect
+++ b/pkg/front_end/testcases/abstract_members.dart.legacy.transformed.expect
@@ -6,8 +6,12 @@
 //
 // pkg/front_end/testcases/abstract_members.dart:33:7: Error: The non-abstract class 'MyClass' is missing implementations for these members:
 //  - A.abstractMethod
-//  - A.property3=
 //  - A.property1=
+//  - A.property3=
+//  - Interface1.interfaceMethod1
+//  - Interface2.interfaceMethod1
+//  - Interface2.interfaceMethod2
+//  - Interface3.interfaceMethod3
 // Try to either
 //  - provide an implementation,
 //  - inherit an implementation from a superclass or mixin,
diff --git a/pkg/front_end/testcases/abstract_members.dart.outline.expect b/pkg/front_end/testcases/abstract_members.dart.outline.expect
index 1c95548..5b799a3 100644
--- a/pkg/front_end/testcases/abstract_members.dart.outline.expect
+++ b/pkg/front_end/testcases/abstract_members.dart.outline.expect
@@ -12,8 +12,12 @@
 //
 // pkg/front_end/testcases/abstract_members.dart:33:7: Error: The non-abstract class 'MyClass' is missing implementations for these members:
 //  - A.abstractMethod
-//  - A.property3=
 //  - A.property1=
+//  - A.property3=
+//  - Interface1.interfaceMethod1
+//  - Interface2.interfaceMethod1
+//  - Interface2.interfaceMethod2
+//  - Interface3.interfaceMethod3
 // Try to either
 //  - provide an implementation,
 //  - inherit an implementation from a superclass or mixin,
@@ -25,12 +29,24 @@
 // pkg/front_end/testcases/abstract_members.dart:21:3: Context: 'A.abstractMethod' is defined here.
 //   abstractMethod();
 //   ^^^^^^^^^^^^^^
-// pkg/front_end/testcases/abstract_members.dart:24:12: Context: 'A.property3=' is defined here.
-//   void set property3(_);
-//            ^^^^^^^^^
 // pkg/front_end/testcases/abstract_members.dart:22:12: Context: 'A.property1=' is defined here.
 //   void set property1(_);
 //            ^^^^^^^^^
+// pkg/front_end/testcases/abstract_members.dart:24:12: Context: 'A.property3=' is defined here.
+//   void set property3(_);
+//            ^^^^^^^^^
+// pkg/front_end/testcases/abstract_members.dart:6:8: Context: 'Interface1.interfaceMethod1' is defined here.
+//   void interfaceMethod1() {}
+//        ^^^^^^^^^^^^^^^^
+// pkg/front_end/testcases/abstract_members.dart:12:7: Context: 'Interface2.interfaceMethod1' is defined here.
+//   var interfaceMethod1;
+//       ^^^^^^^^^^^^^^^^
+// pkg/front_end/testcases/abstract_members.dart:10:8: Context: 'Interface2.interfaceMethod2' is defined here.
+//   void interfaceMethod2() {}
+//        ^^^^^^^^^^^^^^^^
+// pkg/front_end/testcases/abstract_members.dart:16:8: Context: 'Interface3.interfaceMethod3' is defined here.
+//   void interfaceMethod3() {}
+//        ^^^^^^^^^^^^^^^^
 
 library;
 import self as self;