[fasta] Fix checks of abstract methods when noSuchMethod is defined

If an abstract method overrides a concrete method, and the abstract one
is more specific, it's an error, unless there's a user-defined
noSuchMethod, effectively making the abstract method concrete.

Change-Id: I06ebe2de257ef6627529e6e97ca11b768c9647b9
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/106084
Commit-Queue: Dmitry Stefantsov <dmitryas@google.com>
Reviewed-by: Aske Simon Christensen <askesc@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 d61cbca..0960fca 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
@@ -1323,6 +1323,7 @@
       superclasses,
       interfaces,
       maxInheritancePath,
+      hasNoSuchMethod,
     );
   }
 
@@ -1705,6 +1706,8 @@
 
   int get depth => superclasses.length;
 
+  final bool hasNoSuchMethod;
+
   ClassHierarchyNode(
       this.cls,
       this.classMembers,
@@ -1713,7 +1716,8 @@
       this.interfaceSetters,
       this.superclasses,
       this.interfaces,
-      this.maxInheritancePath);
+      this.maxInheritancePath,
+      this.hasNoSuchMethod);
 
   /// Returns a list of all supertypes of [cls], including this node.
   List<ClassHierarchyNode> computeAllSuperNodes(
@@ -2400,7 +2404,7 @@
   Declaration get abstractMember => declarations[1];
 
   Member check(ClassHierarchyBuilder hierarchy) {
-    if (!parent.isAbstract) {
+    if (!parent.isAbstract && !hierarchy.nodes[parent.cls].hasNoSuchMethod) {
       new DelayedOverrideCheck(parent, concreteImplementation, abstractMember)
           .check(hierarchy);
     }
diff --git a/pkg/front_end/testcases/abstract_overrides_concrete_with_no_such_method.dart b/pkg/front_end/testcases/abstract_overrides_concrete_with_no_such_method.dart
new file mode 100644
index 0000000..7bba23c
--- /dev/null
+++ b/pkg/front_end/testcases/abstract_overrides_concrete_with_no_such_method.dart
@@ -0,0 +1,19 @@
+// Copyright (c) 2019, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+abstract class A {
+  A foo() => null;
+}
+
+abstract class B extends A {
+  B foo();
+}
+
+class C {
+  noSuchMethod(_) => null;
+}
+
+class D extends C implements B {}
+
+main() {}
diff --git a/pkg/front_end/testcases/abstract_overrides_concrete_with_no_such_method.dart.hierarchy.expect b/pkg/front_end/testcases/abstract_overrides_concrete_with_no_such_method.dart.hierarchy.expect
new file mode 100644
index 0000000..344af9a
--- /dev/null
+++ b/pkg/front_end/testcases/abstract_overrides_concrete_with_no_such_method.dart.hierarchy.expect
@@ -0,0 +1,105 @@
+Object:
+  superclasses:
+  interfaces:
+  classMembers:
+    Object._haveSameRuntimeType
+    Object.toString
+    Object.runtimeType
+    Object._toString
+    Object._simpleInstanceOf
+    Object._hashCodeRnd
+    Object._instanceOf
+    Object.noSuchMethod
+    Object._objectHashCode
+    Object._identityHashCode
+    Object.hashCode
+    Object._simpleInstanceOfFalse
+    Object._simpleInstanceOfTrue
+    Object.==
+  classSetters:
+
+A:
+  superclasses:
+    Object
+  interfaces:
+  classMembers:
+    Object.toString
+    Object.runtimeType
+    Object._simpleInstanceOf
+    Object._instanceOf
+    A.foo
+    Object.noSuchMethod
+    Object._identityHashCode
+    Object.hashCode
+    Object._simpleInstanceOfFalse
+    Object._simpleInstanceOfTrue
+    Object.==
+  classSetters:
+
+B:
+  superclasses:
+    Object
+      -> A
+  interfaces:
+  classMembers:
+    Object.toString
+    Object.runtimeType
+    Object._simpleInstanceOf
+    Object._instanceOf
+    B.A.foo%B.foo
+    Object.noSuchMethod
+    Object._identityHashCode
+    Object.hashCode
+    Object._simpleInstanceOfFalse
+    Object._simpleInstanceOfTrue
+    Object.==
+  classSetters:
+
+C:
+  superclasses:
+    Object
+  interfaces:
+  classMembers:
+    Object.toString
+    Object.runtimeType
+    Object._simpleInstanceOf
+    Object._instanceOf
+    C.noSuchMethod
+    Object._identityHashCode
+    Object.hashCode
+    Object._simpleInstanceOfFalse
+    Object._simpleInstanceOfTrue
+    Object.==
+  classSetters:
+
+D:
+  Longest path to Object: 3
+  superclasses:
+    Object
+      -> C
+  interfaces: B, A
+  classMembers:
+    Object.toString
+    Object.runtimeType
+    Object._simpleInstanceOf
+    Object._instanceOf
+    C.noSuchMethod
+    Object._identityHashCode
+    Object.hashCode
+    Object._simpleInstanceOfFalse
+    Object._simpleInstanceOfTrue
+    Object.==
+  classSetters:
+  interfaceMembers:
+    Object.toString
+    Object.runtimeType
+    Object._simpleInstanceOf
+    Object._instanceOf
+    B.A.foo%B.foo
+    C.noSuchMethod
+    Object._identityHashCode
+    Object.hashCode
+    Object._simpleInstanceOfFalse
+    Object._simpleInstanceOfTrue
+    Object.==
+  interfaceSetters:
diff --git a/pkg/front_end/testcases/abstract_overrides_concrete_with_no_such_method.dart.legacy.expect b/pkg/front_end/testcases/abstract_overrides_concrete_with_no_such_method.dart.legacy.expect
new file mode 100644
index 0000000..f45f7c3
--- /dev/null
+++ b/pkg/front_end/testcases/abstract_overrides_concrete_with_no_such_method.dart.legacy.expect
@@ -0,0 +1,32 @@
+library;
+import self as self;
+import "dart:core" as core;
+
+abstract class A extends core::Object {
+  synthetic constructor •() → self::A
+    : super core::Object::•()
+    ;
+  method foo() → self::A
+    return null;
+}
+abstract class B extends self::A {
+  synthetic constructor •() → self::B
+    : super self::A::•()
+    ;
+  abstract method foo() → self::B;
+}
+class C extends core::Object {
+  synthetic constructor •() → self::C
+    : super core::Object::•()
+    ;
+  method noSuchMethod(core::Invocation _) → dynamic
+    return null;
+}
+class D extends self::C implements self::B {
+  synthetic constructor •() → self::D
+    : super self::C::•()
+    ;
+  no-such-method-forwarder method foo() → self::B
+    return this.{self::C::noSuchMethod}(new core::_InvocationMirror::_withType(#foo, 0, const <core::Type>[], const <dynamic>[], core::Map::unmodifiable<core::Symbol, dynamic>(const <core::Symbol, dynamic>{}))) as{TypeError} self::B;
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/abstract_overrides_concrete_with_no_such_method.dart.legacy.transformed.expect b/pkg/front_end/testcases/abstract_overrides_concrete_with_no_such_method.dart.legacy.transformed.expect
new file mode 100644
index 0000000..f45f7c3
--- /dev/null
+++ b/pkg/front_end/testcases/abstract_overrides_concrete_with_no_such_method.dart.legacy.transformed.expect
@@ -0,0 +1,32 @@
+library;
+import self as self;
+import "dart:core" as core;
+
+abstract class A extends core::Object {
+  synthetic constructor •() → self::A
+    : super core::Object::•()
+    ;
+  method foo() → self::A
+    return null;
+}
+abstract class B extends self::A {
+  synthetic constructor •() → self::B
+    : super self::A::•()
+    ;
+  abstract method foo() → self::B;
+}
+class C extends core::Object {
+  synthetic constructor •() → self::C
+    : super core::Object::•()
+    ;
+  method noSuchMethod(core::Invocation _) → dynamic
+    return null;
+}
+class D extends self::C implements self::B {
+  synthetic constructor •() → self::D
+    : super self::C::•()
+    ;
+  no-such-method-forwarder method foo() → self::B
+    return this.{self::C::noSuchMethod}(new core::_InvocationMirror::_withType(#foo, 0, const <core::Type>[], const <dynamic>[], core::Map::unmodifiable<core::Symbol, dynamic>(const <core::Symbol, dynamic>{}))) as{TypeError} self::B;
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/abstract_overrides_concrete_with_no_such_method.dart.outline.expect b/pkg/front_end/testcases/abstract_overrides_concrete_with_no_such_method.dart.outline.expect
new file mode 100644
index 0000000..5430c79
--- /dev/null
+++ b/pkg/front_end/testcases/abstract_overrides_concrete_with_no_such_method.dart.outline.expect
@@ -0,0 +1,29 @@
+library;
+import self as self;
+import "dart:core" as core;
+
+abstract class A extends core::Object {
+  synthetic constructor •() → self::A
+    ;
+  method foo() → self::A
+    ;
+}
+abstract class B extends self::A {
+  synthetic constructor •() → self::B
+    ;
+  abstract method foo() → self::B;
+}
+class C extends core::Object {
+  synthetic constructor •() → self::C
+    ;
+  method noSuchMethod(core::Invocation _) → dynamic
+    ;
+}
+class D extends self::C implements self::B {
+  synthetic constructor •() → self::D
+    ;
+  no-such-method-forwarder method foo() → self::B
+    return this.{self::C::noSuchMethod}(new core::_InvocationMirror::_withType(#foo, 0, const <core::Type>[], const <dynamic>[], core::Map::unmodifiable<core::Symbol, dynamic>(const <core::Symbol, dynamic>{}))) as{TypeError} self::B;
+}
+static method main() → dynamic
+  ;
diff --git a/pkg/front_end/testcases/abstract_overrides_concrete_with_no_such_method.dart.strong.expect b/pkg/front_end/testcases/abstract_overrides_concrete_with_no_such_method.dart.strong.expect
new file mode 100644
index 0000000..f45f7c3
--- /dev/null
+++ b/pkg/front_end/testcases/abstract_overrides_concrete_with_no_such_method.dart.strong.expect
@@ -0,0 +1,32 @@
+library;
+import self as self;
+import "dart:core" as core;
+
+abstract class A extends core::Object {
+  synthetic constructor •() → self::A
+    : super core::Object::•()
+    ;
+  method foo() → self::A
+    return null;
+}
+abstract class B extends self::A {
+  synthetic constructor •() → self::B
+    : super self::A::•()
+    ;
+  abstract method foo() → self::B;
+}
+class C extends core::Object {
+  synthetic constructor •() → self::C
+    : super core::Object::•()
+    ;
+  method noSuchMethod(core::Invocation _) → dynamic
+    return null;
+}
+class D extends self::C implements self::B {
+  synthetic constructor •() → self::D
+    : super self::C::•()
+    ;
+  no-such-method-forwarder method foo() → self::B
+    return this.{self::C::noSuchMethod}(new core::_InvocationMirror::_withType(#foo, 0, const <core::Type>[], const <dynamic>[], core::Map::unmodifiable<core::Symbol, dynamic>(const <core::Symbol, dynamic>{}))) as{TypeError} self::B;
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/abstract_overrides_concrete_with_no_such_method.dart.strong.transformed.expect b/pkg/front_end/testcases/abstract_overrides_concrete_with_no_such_method.dart.strong.transformed.expect
new file mode 100644
index 0000000..f45f7c3
--- /dev/null
+++ b/pkg/front_end/testcases/abstract_overrides_concrete_with_no_such_method.dart.strong.transformed.expect
@@ -0,0 +1,32 @@
+library;
+import self as self;
+import "dart:core" as core;
+
+abstract class A extends core::Object {
+  synthetic constructor •() → self::A
+    : super core::Object::•()
+    ;
+  method foo() → self::A
+    return null;
+}
+abstract class B extends self::A {
+  synthetic constructor •() → self::B
+    : super self::A::•()
+    ;
+  abstract method foo() → self::B;
+}
+class C extends core::Object {
+  synthetic constructor •() → self::C
+    : super core::Object::•()
+    ;
+  method noSuchMethod(core::Invocation _) → dynamic
+    return null;
+}
+class D extends self::C implements self::B {
+  synthetic constructor •() → self::D
+    : super self::C::•()
+    ;
+  no-such-method-forwarder method foo() → self::B
+    return this.{self::C::noSuchMethod}(new core::_InvocationMirror::_withType(#foo, 0, const <core::Type>[], const <dynamic>[], core::Map::unmodifiable<core::Symbol, dynamic>(const <core::Symbol, dynamic>{}))) as{TypeError} self::B;
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/text_serialization.status b/pkg/front_end/testcases/text_serialization.status
index 32535ad..1fd5eae 100644
--- a/pkg/front_end/testcases/text_serialization.status
+++ b/pkg/front_end/testcases/text_serialization.status
@@ -8,6 +8,7 @@
 
 DeltaBlue: TextSerializationFailure # Was: Pass
 abstract_members: TypeCheckError
+abstract_overrides_concrete_with_no_such_method: TextSerializationFailure
 accessors: TextSerializationFailure # Was: RuntimeError
 ambiguous_exports: TextSerializationFailure # Was: RuntimeError # Expected, this file exports two main methods.
 annotation_eof: TextSerializationFailure # Was: Pass