Improve detection of conflicts with inherited members and setters
Change-Id: Ifb2fc2ca2e3d812a8e2a56e4fc43c8cfd02f15d5
Reviewed-on: https://dart-review.googlesource.com/c/88942
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 5ee5ae1..89f5f51 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
@@ -54,9 +54,12 @@
/// Language Specification](
/// ../../../../../../docs/language/dartLangSpec.tex#classMemberConflicts).
bool isInheritanceConflict(Declaration a, Declaration b) {
+ if (memberKind(a.target) == memberKind(b.target)) return false;
if (a.isField) return !(b.isField || b.isGetter || b.isSetter);
if (b.isField) return !(a.isField || a.isGetter || a.isSetter);
- return memberKind(a.target) != memberKind(b.target);
+ if (a.isSetter) return !(b.isGetter || b.isSetter);
+ if (b.isSetter) return !(a.isGetter || a.isSetter);
+ return true;
}
bool impliesSetter(Declaration declaration) {
@@ -96,7 +99,8 @@
reportInheritanceConflict(cls, a, b);
}
Declaration result = a;
- if (mergeKind == MergeKind.interfaces) {
+ if (mergeKind == MergeKind.accessors) {
+ } else if (mergeKind == MergeKind.interfaces) {
// TODO(ahe): Combine the signatures of a and b. See the section named
// "Combined Member Signatures" in [Dart Programming Language
// Specification](
@@ -211,17 +215,38 @@
// [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.
+
+ /// Members (excluding setters) declared in [cls].
List<Declaration> localMembers =
new List<Declaration>.from(scope.local.values)
..sort(compareDeclarations);
+
+ /// Setters declared in [cls].
List<Declaration> localSetters =
new List<Declaration>.from(scope.setters.values)
..sort(compareDeclarations);
+
+ // Add implied setters from fields in [localMembers].
localSetters = mergeAccessors(cls, localMembers, localSetters);
+
+ /// Members (excluding setters) declared in [cls] or its superclasses. This
+ /// includes static methods of [cls], but not its superclasses.
List<Declaration> classMembers;
+
+ /// Setters declared in [cls] or its superclasses. This includes static
+ /// setters of [cls], but not its superclasses.
List<Declaration> classSetters;
+
+ /// Members (excluding setters) inherited from interfaces. This contains no static
+ /// members. Is null if no interfaces are implemented by this class or its
+ /// superclasses.
List<Declaration> interfaceMembers;
+
+ /// Setters inherited from interfaces. This contains no static setters. Is
+ /// null if no interfaces are implemented by this class or its
+ /// superclasses.
List<Declaration> interfaceSetters;
+
if (supernode == null) {
// This should be Object.
classMembers = localMembers;
@@ -231,6 +256,16 @@
cls, localMembers, supernode.classMembers, MergeKind.superclass);
classSetters = merge(
cls, localSetters, supernode.classSetters, MergeKind.superclass);
+
+ // Check if local members conflict with inherited setters. This check has
+ // already been performed in the superclass, so we only need to check the
+ // local members.
+ merge(cls, localMembers, classSetters, MergeKind.accessors);
+
+ // Check if local setters conflict with inherited members. As above, we
+ // only need to check the local setters.
+ merge(cls, localSetters, classMembers, MergeKind.accessors);
+
List<KernelTypeBuilder> interfaces = cls.interfaces;
if (interfaces != null) {
MergeResult result = mergeInterfaces(cls, supernode, interfaces);
@@ -243,10 +278,18 @@
if (interfaceMembers != null) {
interfaceMembers =
merge(cls, classMembers, interfaceMembers, MergeKind.supertypes);
+
+ // Check if class setters conflict with members inherited from
+ // interfaces.
+ merge(cls, classSetters, interfaceMembers, MergeKind.accessors);
}
- if (interfaceMembers != null) {
+ if (interfaceSetters != null) {
interfaceSetters =
merge(cls, classSetters, interfaceSetters, MergeKind.supertypes);
+
+ // Check if class members conflict with setters inherited from
+ // interfaces.
+ merge(cls, classMembers, interfaceSetters, MergeKind.accessors);
}
}
nodes[cls] = new ClassHierarchyNode(cls, scope, classMembers, classSetters,
@@ -483,6 +526,8 @@
}
j++;
}
+ if (aList.isEmpty && storeIndex == bList.length) return bList;
+ if (bList.isEmpty && storeIndex == aList.length) return aList;
return result..length = storeIndex;
}
}
@@ -536,4 +581,8 @@
/// Merging class members with interface members.
supertypes,
+
+ /// Merging members with inherited setters, or setters with inherited
+ /// members.
+ accessors,
}
diff --git a/pkg/front_end/testcases/abstract_members.dart.legacy.expect b/pkg/front_end/testcases/abstract_members.dart.legacy.expect
index dd5cc31..867d44f 100644
--- a/pkg/front_end/testcases/abstract_members.dart.legacy.expect
+++ b/pkg/front_end/testcases/abstract_members.dart.legacy.expect
@@ -100,6 +100,26 @@
// var interfaceMethod1;
// ^^^^^^^^^^^^^^^^
//
+// pkg/front_end/testcases/abstract_members.dart:72:16: Error: Can't inherit members that conflict with each other.
+// abstract class F extends E implements Interface1 {}
+// ^
+// pkg/front_end/testcases/abstract_members.dart:67:12: Context: This is one inherited member.
+// void set interfaceMethod1(_) {}
+// ^^^^^^^^^^^^^^^^
+// pkg/front_end/testcases/abstract_members.dart:6:8: Context: This is the other inherited member.
+// void interfaceMethod1() {}
+// ^^^^^^^^^^^^^^^^
+//
+// pkg/front_end/testcases/abstract_members.dart:84:16: Error: Can't inherit members that conflict with each other.
+// abstract class H extends G implements Foo {}
+// ^
+// pkg/front_end/testcases/abstract_members.dart:79:12: Context: This is one inherited member.
+// void set foo(_) {}
+// ^^^
+// pkg/front_end/testcases/abstract_members.dart:75:8: Context: This is the other inherited member.
+// void foo() {}
+// ^^^
+//
// pkg/front_end/testcases/abstract_members.dart:96:16: Error: Can't inherit members that conflict with each other.
// abstract class J extends I implements Bar {}
// ^
@@ -155,6 +175,14 @@
// abstract class D extends C implements Interface2 {}
// ^
//
+// pkg/front_end/testcases/abstract_members.dart:72:16: Error: Can't inherit members that conflict with each other.
+// abstract class F extends E implements Interface1 {}
+// ^
+//
+// pkg/front_end/testcases/abstract_members.dart:84:16: Error: Can't inherit members that conflict with each other.
+// abstract class H extends G implements Foo {}
+// ^
+//
// pkg/front_end/testcases/abstract_members.dart:96:16: Error: Can't inherit members that conflict with each other.
// abstract class J extends I implements Bar {}
// ^
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 22ee2e5..9c3e181 100644
--- a/pkg/front_end/testcases/abstract_members.dart.legacy.transformed.expect
+++ b/pkg/front_end/testcases/abstract_members.dart.legacy.transformed.expect
@@ -43,6 +43,14 @@
// abstract class D extends C implements Interface2 {}
// ^
//
+// pkg/front_end/testcases/abstract_members.dart:72:16: Error: Can't inherit members that conflict with each other.
+// abstract class F extends E implements Interface1 {}
+// ^
+//
+// pkg/front_end/testcases/abstract_members.dart:84:16: Error: Can't inherit members that conflict with each other.
+// abstract class H extends G implements Foo {}
+// ^
+//
// pkg/front_end/testcases/abstract_members.dart:96:16: Error: Can't inherit members that conflict with each other.
// abstract class J extends I implements Bar {}
// ^
diff --git a/pkg/front_end/testcases/abstract_members.dart.outline.expect b/pkg/front_end/testcases/abstract_members.dart.outline.expect
index a352fbd..9ef4ea2 100644
--- a/pkg/front_end/testcases/abstract_members.dart.outline.expect
+++ b/pkg/front_end/testcases/abstract_members.dart.outline.expect
@@ -100,6 +100,26 @@
// var interfaceMethod1;
// ^^^^^^^^^^^^^^^^
//
+// pkg/front_end/testcases/abstract_members.dart:72:16: Error: Can't inherit members that conflict with each other.
+// abstract class F extends E implements Interface1 {}
+// ^
+// pkg/front_end/testcases/abstract_members.dart:67:12: Context: This is one inherited member.
+// void set interfaceMethod1(_) {}
+// ^^^^^^^^^^^^^^^^
+// pkg/front_end/testcases/abstract_members.dart:6:8: Context: This is the other inherited member.
+// void interfaceMethod1() {}
+// ^^^^^^^^^^^^^^^^
+//
+// pkg/front_end/testcases/abstract_members.dart:84:16: Error: Can't inherit members that conflict with each other.
+// abstract class H extends G implements Foo {}
+// ^
+// pkg/front_end/testcases/abstract_members.dart:79:12: Context: This is one inherited member.
+// void set foo(_) {}
+// ^^^
+// pkg/front_end/testcases/abstract_members.dart:75:8: Context: This is the other inherited member.
+// void foo() {}
+// ^^^
+//
// pkg/front_end/testcases/abstract_members.dart:96:16: Error: Can't inherit members that conflict with each other.
// abstract class J extends I implements Bar {}
// ^
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 6742c9b..1edaa65 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
@@ -6,12 +6,34 @@
// 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) {}
// ^^^
+//
+// 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.
+// void set foo(int x);
+// ^^^
+//
+// pkg/front_end/testcases/no_such_method_forwarders/setter_not_shadowed_by_method.dart:12:12: Error: Can't declare a member that conflicts with an inherited one.
+// void set foo(int x);
+// ^^^
+// pkg/front_end/testcases/no_such_method_forwarders/setter_not_shadowed_by_method.dart:10:8: Context: This is the inherited member.
+// void foo(int x) {}
+// ^^^
// Unhandled errors:
//
// 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: 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: Can't declare a member that conflicts with an inherited one.
+// 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 e9e7677..882dc86 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
@@ -3,6 +3,14 @@
// 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: 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: Can't declare a member that conflicts with an inherited one.
+// 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 d8c8b38..fbde11f 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
@@ -6,6 +6,20 @@
// 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) {}
// ^^^
+//
+// 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.
+// void set foo(int x);
+// ^^^
+//
+// pkg/front_end/testcases/no_such_method_forwarders/setter_not_shadowed_by_method.dart:12:12: Error: Can't declare a member that conflicts with an inherited one.
+// void set foo(int x);
+// ^^^
+// pkg/front_end/testcases/no_such_method_forwarders/setter_not_shadowed_by_method.dart:10:8: Context: This is the inherited member.
+// 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 473cece..f359ff5 100644
--- a/pkg/front_end/testcases/rasta/super.dart.legacy.expect
+++ b/pkg/front_end/testcases/rasta/super.dart.legacy.expect
@@ -7,6 +7,20 @@
// void n() {}
// ^
//
+// 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.
+// set n(_) {}
+// ^
+//
+// pkg/front_end/testcases/rasta/super.dart:27:7: Error: Can't declare a member that conflicts with an inherited one.
+// set n(_) {}
+// ^
+// pkg/front_end/testcases/rasta/super.dart:26:8: Context: This is the inherited member.
+// void n() {}
+// ^
+//
// pkg/front_end/testcases/rasta/super.dart:43:5: Error: '+' is not a prefix operator.
// Try removing '+'.
// +super;
@@ -243,6 +257,14 @@
// set n(_) {}
// ^
//
+// 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: Can't declare a member that conflicts with an inherited one.
+// set n(_) {}
+// ^
+//
// pkg/front_end/testcases/rasta/super.dart:43:5: Error: '+' is not a prefix operator.
// Try removing '+'.
// +super;
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 f92047d..3228f16 100644
--- a/pkg/front_end/testcases/rasta/super.dart.legacy.transformed.expect
+++ b/pkg/front_end/testcases/rasta/super.dart.legacy.transformed.expect
@@ -4,6 +4,14 @@
// set n(_) {}
// ^
//
+// 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: Can't declare a member that conflicts with an inherited one.
+// set n(_) {}
+// ^
+//
// pkg/front_end/testcases/rasta/super.dart:43:5: Error: '+' is not a prefix operator.
// Try removing '+'.
// +super;
diff --git a/pkg/front_end/testcases/rasta/super.dart.outline.expect b/pkg/front_end/testcases/rasta/super.dart.outline.expect
index c3496aa..0203b77 100644
--- a/pkg/front_end/testcases/rasta/super.dart.outline.expect
+++ b/pkg/front_end/testcases/rasta/super.dart.outline.expect
@@ -6,6 +6,20 @@
// pkg/front_end/testcases/rasta/super.dart:26:8: Context: Previous declaration of 'n'.
// void n() {}
// ^
+//
+// 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.
+// set n(_) {}
+// ^
+//
+// pkg/front_end/testcases/rasta/super.dart:27:7: Error: Can't declare a member that conflicts with an inherited one.
+// set n(_) {}
+// ^
+// pkg/front_end/testcases/rasta/super.dart:26:8: Context: This is the inherited member.
+// void n() {}
+// ^
library;
import self as self;