Add non-final fields to setters

Change-Id: Ib42064cf762dae6eb0ff5bda4459ce69a9f78b26
Reviewed-on: https://dart-review.googlesource.com/c/88717
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 772178d..a6074c2 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
@@ -17,6 +17,8 @@
         messageInheritedMembersConflict,
         messageInheritedMembersConflictCause1,
         messageInheritedMembersConflictCause2,
+        templateDuplicatedDeclaration,
+        templateDuplicatedDeclarationCause,
         templateMissingImplementationCause,
         templateMissingImplementationNotAbstract;
 
@@ -57,6 +59,10 @@
   return memberKind(a.target) != memberKind(b.target);
 }
 
+bool impliesSetter(Declaration declaration) {
+  return declaration.isField && !(declaration.isFinal || declaration.isConst);
+}
+
 class ClassHierarchyBuilder {
   final Map<KernelClassBuilder, ClassHierarchyNode> nodes =
       <KernelClassBuilder, ClassHierarchyNode>{};
@@ -203,6 +209,7 @@
     List<Declaration> localSetters =
         new List<Declaration>.from(scope.setters.values)
           ..sort(compareDeclarations);
+    localSetters = mergeAccessors(cls, localMembers, localSetters);
     List<Declaration> classMembers;
     List<Declaration> classSetters;
     List<Declaration> interfaceMembers;
@@ -236,7 +243,6 @@
     }
     nodes[cls] = new ClassHierarchyNode(cls, scope, classMembers, classSetters,
         interfaceMembers, interfaceSetters);
-    mergeAccessors(cls, classMembers, classSetters);
 
     if (abstractMemberCount != 0 && !cls.isAbstract) {
       if (!hasNoSuchMethod) {
@@ -298,71 +304,66 @@
     return input.single;
   }
 
-  /// Merge [and check] accessors. This entails removing setters corresponding
-  /// to fields, and checking that setters don't override regular methods.
-  void mergeAccessors(KernelClassBuilder cls, List<Declaration> members,
-      List<Declaration> setters) {
-    List<Declaration> overriddenSetters;
+  /// Merge [and check] accessors. This entails copying mutable fields to
+  /// setters to simulate implied setters, and checking that setters don't
+  /// override regular methods.
+  List<Declaration> mergeAccessors(KernelClassBuilder cls,
+      List<Declaration> members, List<Declaration> setters) {
+    final List<Declaration> mergedSetters = new List<Declaration>.filled(
+        members.length + setters.length, null,
+        growable: true);
+    int storeIndex = 0;
     int i = 0;
     int j = 0;
     while (i < members.length && j < setters.length) {
-      Declaration member = members[i];
-      Declaration setter = setters[j];
+      final Declaration member = members[i];
+      final Declaration setter = setters[j];
       final int compare = compareDeclarations(member, setter);
       if (compare == 0) {
-        if (member.isField) {
-          // TODO(ahe): What happens if we have both a field and a setter
-          // declared in the same class?
-          if (!member.isFinal && !member.isConst) {
-            // The field overrides the setter.
-            (overriddenSetters ??= <Declaration>[]).add(setter);
-            Member target = setter.target;
-            if (target.isAbstract) {
-              abstractMemberCount--;
-            }
-          }
-        } else if (!member.isGetter) {
-          String name = member.fullNameForErrors;
+        if (member.isField ? impliesSetter(member) : !member.isGetter) {
+          // [member] conflicts with [setter].
+          final String name = member.fullNameForErrors;
           cls.library.addProblem(
-              messageDeclaredMemberConflictsWithInheritedMember,
-              member.charOffset,
+              templateDuplicatedDeclaration.withArguments(name),
+              setter.charOffset,
               name.length,
-              member.fileUri,
+              setter.fileUri,
               context: <LocatedMessage>[
-                messageDeclaredMemberConflictsWithInheritedMemberCause
+                templateDuplicatedDeclarationCause
+                    .withArguments(name)
                     .withLocation(
-                        setter.fileUri, setter.charOffset, name.length)
+                        member.fileUri, member.charOffset, name.length)
               ]);
         }
+        mergedSetters[storeIndex++] = setter;
         i++;
         j++;
       } else if (compare < 0) {
+        if (impliesSetter(member)) {
+          mergedSetters[storeIndex++] = member;
+        }
         i++;
       } else {
+        mergedSetters[storeIndex++] = setters[j];
         j++;
       }
     }
-    // One of of the two lists is now exhausted. What remains in the other list
-    // cannot be a conflict.
+    while (i < members.length) {
+      final Declaration member = members[i];
+      if (impliesSetter(member)) {
+        mergedSetters[storeIndex++] = member;
+      }
+      i++;
+    }
+    while (j < setters.length) {
+      mergedSetters[storeIndex++] = setters[j];
+      j++;
+    }
 
-    if (overriddenSetters != null) {
-      // Remove [overriddenSetters] from [setters] by copying [setters]
-      // to itself.
-      int i = 0;
-      int j = 0;
-      int storeIndex = 0;
-      while (i < setters.length && j < overriddenSetters.length) {
-        if (setters[i] == overriddenSetters[j]) {
-          i++;
-          j++;
-        } else {
-          setters[storeIndex++] = setters[i++];
-        }
-      }
-      while (i < setters.length) {
-        setters[storeIndex++] = setters[i++];
-      }
-      setters.length = storeIndex;
+    if (storeIndex == j) {
+      return setters;
+    } else {
+      return mergedSetters..length = storeIndex;
     }
   }
 
diff --git a/pkg/front_end/testcases/no_such_method_forwarders/setter_not_shadowed_by_method.dart.legacy.expect b/pkg/front_end/testcases/no_such_method_forwarders/setter_not_shadowed_by_method.dart.legacy.expect
index af73d67..6742c9b 100644
--- a/pkg/front_end/testcases/no_such_method_forwarders/setter_not_shadowed_by_method.dart.legacy.expect
+++ b/pkg/front_end/testcases/no_such_method_forwarders/setter_not_shadowed_by_method.dart.legacy.expect
@@ -1,17 +1,17 @@
 // Formatted problems:
 //
-// pkg/front_end/testcases/no_such_method_forwarders/setter_not_shadowed_by_method.dart:10:8: Error: Can't declare a member that conflicts with an inherited one.
-//   void foo(int x) {}
-//        ^^^
-// pkg/front_end/testcases/no_such_method_forwarders/setter_not_shadowed_by_method.dart:12:12: Context: This is the inherited member.
+// pkg/front_end/testcases/no_such_method_forwarders/setter_not_shadowed_by_method.dart:12:12: Error: 'foo' is already declared in this scope.
 //   void set foo(int x);
 //            ^^^
+// pkg/front_end/testcases/no_such_method_forwarders/setter_not_shadowed_by_method.dart:10:8: Context: Previous declaration of 'foo'.
+//   void foo(int x) {}
+//        ^^^
 
 // Unhandled errors:
 //
-// pkg/front_end/testcases/no_such_method_forwarders/setter_not_shadowed_by_method.dart:10:8: Error: Can't declare a member that conflicts with an inherited one.
-//   void foo(int x) {}
-//        ^^^
+// pkg/front_end/testcases/no_such_method_forwarders/setter_not_shadowed_by_method.dart:12:12: Error: 'foo' is already declared in this scope.
+//   void set foo(int x);
+//            ^^^
 
 library;
 import self as self;
diff --git a/pkg/front_end/testcases/no_such_method_forwarders/setter_not_shadowed_by_method.dart.legacy.transformed.expect b/pkg/front_end/testcases/no_such_method_forwarders/setter_not_shadowed_by_method.dart.legacy.transformed.expect
index 37f1f55..e9e7677 100644
--- a/pkg/front_end/testcases/no_such_method_forwarders/setter_not_shadowed_by_method.dart.legacy.transformed.expect
+++ b/pkg/front_end/testcases/no_such_method_forwarders/setter_not_shadowed_by_method.dart.legacy.transformed.expect
@@ -1,8 +1,8 @@
 // Unhandled errors:
 //
-// pkg/front_end/testcases/no_such_method_forwarders/setter_not_shadowed_by_method.dart:10:8: Error: Can't declare a member that conflicts with an inherited one.
-//   void foo(int x) {}
-//        ^^^
+// pkg/front_end/testcases/no_such_method_forwarders/setter_not_shadowed_by_method.dart:12:12: Error: 'foo' is already declared in this scope.
+//   void set foo(int x);
+//            ^^^
 
 library;
 import self as self;
diff --git a/pkg/front_end/testcases/no_such_method_forwarders/setter_not_shadowed_by_method.dart.outline.expect b/pkg/front_end/testcases/no_such_method_forwarders/setter_not_shadowed_by_method.dart.outline.expect
index 51ebf0c..d8c8b38 100644
--- a/pkg/front_end/testcases/no_such_method_forwarders/setter_not_shadowed_by_method.dart.outline.expect
+++ b/pkg/front_end/testcases/no_such_method_forwarders/setter_not_shadowed_by_method.dart.outline.expect
@@ -1,11 +1,11 @@
 // Formatted problems:
 //
-// pkg/front_end/testcases/no_such_method_forwarders/setter_not_shadowed_by_method.dart:10:8: Error: Can't declare a member that conflicts with an inherited one.
-//   void foo(int x) {}
-//        ^^^
-// pkg/front_end/testcases/no_such_method_forwarders/setter_not_shadowed_by_method.dart:12:12: Context: This is the inherited member.
+// pkg/front_end/testcases/no_such_method_forwarders/setter_not_shadowed_by_method.dart:12:12: Error: 'foo' is already declared in this scope.
 //   void set foo(int x);
 //            ^^^
+// pkg/front_end/testcases/no_such_method_forwarders/setter_not_shadowed_by_method.dart:10:8: Context: Previous declaration of 'foo'.
+//   void foo(int x) {}
+//        ^^^
 
 library;
 import self as self;
diff --git a/pkg/front_end/testcases/rasta/super.dart.legacy.expect b/pkg/front_end/testcases/rasta/super.dart.legacy.expect
index ffacfe6..473cece 100644
--- a/pkg/front_end/testcases/rasta/super.dart.legacy.expect
+++ b/pkg/front_end/testcases/rasta/super.dart.legacy.expect
@@ -1,11 +1,11 @@
 // Formatted problems:
 //
-// pkg/front_end/testcases/rasta/super.dart:26:8: Error: Can't declare a member that conflicts with an inherited one.
-//   void n() {}
-//        ^
-// pkg/front_end/testcases/rasta/super.dart:27:7: Context: This is the inherited member.
+// pkg/front_end/testcases/rasta/super.dart:27:7: Error: 'n' is already declared in this scope.
 //   set n(_) {}
 //       ^
+// pkg/front_end/testcases/rasta/super.dart:26:8: Context: Previous declaration of 'n'.
+//   void n() {}
+//        ^
 //
 // pkg/front_end/testcases/rasta/super.dart:43:5: Error: '+' is not a prefix operator.
 // Try removing '+'.
@@ -239,9 +239,9 @@
 
 // Unhandled errors:
 //
-// pkg/front_end/testcases/rasta/super.dart:26:8: Error: Can't declare a member that conflicts with an inherited one.
-//   void n() {}
-//        ^
+// pkg/front_end/testcases/rasta/super.dart:27:7: Error: 'n' is already declared in this scope.
+//   set n(_) {}
+//       ^
 //
 // pkg/front_end/testcases/rasta/super.dart:43:5: Error: '+' is not a prefix operator.
 // Try removing '+'.
diff --git a/pkg/front_end/testcases/rasta/super.dart.legacy.transformed.expect b/pkg/front_end/testcases/rasta/super.dart.legacy.transformed.expect
index 3a83d7b..f92047d 100644
--- a/pkg/front_end/testcases/rasta/super.dart.legacy.transformed.expect
+++ b/pkg/front_end/testcases/rasta/super.dart.legacy.transformed.expect
@@ -1,8 +1,8 @@
 // Unhandled errors:
 //
-// pkg/front_end/testcases/rasta/super.dart:26:8: Error: Can't declare a member that conflicts with an inherited one.
-//   void n() {}
-//        ^
+// pkg/front_end/testcases/rasta/super.dart:27:7: Error: 'n' is already declared in this scope.
+//   set n(_) {}
+//       ^
 //
 // pkg/front_end/testcases/rasta/super.dart:43:5: Error: '+' is not a prefix operator.
 // Try removing '+'.
diff --git a/pkg/front_end/testcases/rasta/super.dart.outline.expect b/pkg/front_end/testcases/rasta/super.dart.outline.expect
index 8798c49..c3496aa 100644
--- a/pkg/front_end/testcases/rasta/super.dart.outline.expect
+++ b/pkg/front_end/testcases/rasta/super.dart.outline.expect
@@ -1,11 +1,11 @@
 // Formatted problems:
 //
-// pkg/front_end/testcases/rasta/super.dart:26:8: Error: Can't declare a member that conflicts with an inherited one.
-//   void n() {}
-//        ^
-// pkg/front_end/testcases/rasta/super.dart:27:7: Context: This is the inherited member.
+// pkg/front_end/testcases/rasta/super.dart:27:7: Error: 'n' is already declared in this scope.
 //   set n(_) {}
 //       ^
+// pkg/front_end/testcases/rasta/super.dart:26:8: Context: Previous declaration of 'n'.
+//   void n() {}
+//        ^
 
 library;
 import self as self;