Issue 35090. Add noSuchMethod forwarders to implemented members.

R=brianwilkerson@google.com

Bug: https://github.com/dart-lang/sdk/issues/35090
Change-Id: I1a72d70effe49111921a0db2a2e5396197d3a51b
Reviewed-on: https://dart-review.googlesource.com/c/88542
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
diff --git a/pkg/analyzer/lib/src/dart/element/inheritance_manager2.dart b/pkg/analyzer/lib/src/dart/element/inheritance_manager2.dart
index d523d07..799dbc8 100644
--- a/pkg/analyzer/lib/src/dart/element/inheritance_manager2.dart
+++ b/pkg/analyzer/lib/src/dart/element/inheritance_manager2.dart
@@ -30,6 +30,8 @@
 
 /// Manages knowledge about interface types and their members.
 class InheritanceManager2 {
+  static final _noSuchMethodName = Name(null, 'noSuchMethod');
+
   final TypeSystem _typeSystem;
 
   /// Cached instance interfaces for [InterfaceType].
@@ -39,12 +41,6 @@
   /// self-referencing cycles.
   final Set<ClassElement> _processingClasses = new Set<ClassElement>();
 
-  /// Cached implemented members for [InterfaceType].
-  final Map<InterfaceType, Map<Name, FunctionType>> _implemented = {};
-
-  /// Cached member implemented in the mixin.
-  final Map<InterfaceType, Map<Name, FunctionType>> _mixinMembers = {};
-
   InheritanceManager2(this._typeSystem);
 
   /// Return the member with the given [name] that the [type] inherits from the
@@ -74,49 +70,71 @@
     Map<Name, List<FunctionType>> namedCandidates = {};
     List<Map<Name, FunctionType>> superImplemented = [];
     Map<Name, FunctionType> declared;
+    Interface superInterface;
+    Map<Name, FunctionType> implemented;
+    Map<Name, FunctionType> implementedForMixing;
     try {
       // If a class declaration has a member declaration, the signature of that
       // member declaration becomes the signature in the interface.
       declared = _getTypeMembers(type);
 
       for (var interface in type.interfaces) {
-        _addCandidates(namedCandidates, interface);
+        var interfaceObj = getInterface(interface);
+        _addCandidates(namedCandidates, interfaceObj);
       }
 
-      if (type.element.isMixin) {
+      if (classElement.isMixin) {
         for (var constraint in type.superclassConstraints) {
-          _addCandidates(namedCandidates, constraint);
+          var interfaceObj = getInterface(constraint);
+          _addCandidates(namedCandidates, interfaceObj);
         }
 
+        implemented = {};
+
         // `mixin M on S1, S2 {}` can call using `super` any instance member
         // from its superclass constraints, whether it is abstract or concrete.
         Map<Name, FunctionType> mixinSuperClass = {};
         _findMostSpecificFromNamedCandidates(mixinSuperClass, namedCandidates);
         superImplemented.add(mixinSuperClass);
       } else {
-        Map<Name, FunctionType> implemented;
-
         if (type.superclass != null) {
-          _addCandidates(namedCandidates, type.superclass);
+          superInterface = getInterface(type.superclass);
+          _addCandidates(namedCandidates, superInterface);
 
-          implemented = _getImplemented(type.superclass);
+          implemented = superInterface.implemented;
           superImplemented.add(implemented);
+        } else {
+          implemented = {};
         }
 
+        implementedForMixing = {};
         for (var mixin in type.mixins) {
-          _addCandidates(namedCandidates, mixin);
+          var interfaceObj = getInterface(mixin);
+          _addCandidates(namedCandidates, interfaceObj);
 
-          var implementedInMixin = _getImplemented(mixin);
           implemented = <Name, FunctionType>{}
             ..addAll(implemented)
-            ..addAll(implementedInMixin);
+            ..addAll(interfaceObj.implementedForMixing);
           superImplemented.add(implemented);
+          implementedForMixing.addAll(interfaceObj.implementedForMixing);
         }
       }
     } finally {
       _processingClasses.remove(classElement);
     }
 
+    var thisImplemented = <Name, FunctionType>{};
+    _addImplemented(thisImplemented, type);
+
+    if (classElement.isMixin) {
+      implementedForMixing = thisImplemented;
+    } else {
+      implementedForMixing.addAll(thisImplemented);
+    }
+
+    implemented = <Name, FunctionType>{}..addAll(implemented);
+    _addImplemented(implemented, type);
+
     // If a class declaration does not have a member declaration with a
     // particular name, but some super-interfaces do have a member with that
     // name, it's a compile-time error if there is no signature among the
@@ -129,9 +147,31 @@
       namedCandidates,
     );
 
+    var noSuchMethodForwarders = Set<Name>();
+    if (classElement.isAbstract) {
+      if (superInterface != null) {
+        noSuchMethodForwarders = superInterface.noSuchMethodForwarders;
+      }
+    } else {
+      var noSuchMethod = implemented[_noSuchMethodName]?.element;
+      if (noSuchMethod != null && !_isDeclaredInObject(noSuchMethod)) {
+        var superForwarders = superInterface?.noSuchMethodForwarders;
+        for (var name in map.keys) {
+          if (!implemented.containsKey(name) ||
+              superForwarders != null && superForwarders.contains(name)) {
+            implemented[name] = map[name];
+            noSuchMethodForwarders.add(name);
+          }
+        }
+      }
+    }
+
     var interface = new Interface._(
       map,
       declared,
+      implemented,
+      noSuchMethodForwarders,
+      implementedForMixing,
       namedCandidates,
       superImplemented,
       conflicts ?? const [],
@@ -158,17 +198,18 @@
     int forMixinIndex: -1,
     bool forSuper: false,
   }) {
+    var interface = getInterface(type);
     if (forSuper) {
-      var superImplemented = getInterface(type)._superImplemented;
+      var superImplemented = interface._superImplemented;
       if (forMixinIndex >= 0) {
         return superImplemented[forMixinIndex][name];
       }
       return superImplemented.last[name];
     }
     if (concrete) {
-      return _getImplemented(type)[name];
+      return interface.implemented[name];
     }
-    return getInterface(type).map[name];
+    return interface.map[name];
   }
 
   /// Return all members of mixins, superclasses, and interfaces that a member
@@ -191,14 +232,33 @@
   }
 
   void _addCandidates(
-      Map<Name, List<FunctionType>> namedCandidates, InterfaceType type) {
-    var map = getInterface(type).map;
+      Map<Name, List<FunctionType>> namedCandidates, Interface interface) {
+    var map = interface.map;
     for (var name in map.keys) {
       var candidate = map[name];
       _addCandidate(namedCandidates, name, candidate);
     }
   }
 
+  void _addImplemented(
+      Map<Name, FunctionType> implemented, InterfaceType type) {
+    var libraryUri = type.element.librarySource.uri;
+
+    void addMember(ExecutableElement member) {
+      if (!member.isAbstract && !member.isStatic) {
+        var name = new Name(libraryUri, member.name);
+        implemented[name] = member.type;
+      }
+    }
+
+    void addMembers(InterfaceType type) {
+      type.methods.forEach(addMember);
+      type.accessors.forEach(addMember);
+    }
+
+    addMembers(type);
+  }
+
   /// Check that all [candidates] for the given [name] have the same kind, all
   /// getters, all methods, or all setter.  If a conflict found, return the
   /// new [Conflict] instance that describes it.
@@ -301,83 +361,6 @@
     return conflicts;
   }
 
-  Map<Name, FunctionType> _getImplemented(InterfaceType type) {
-    var implemented = _implemented[type];
-    if (implemented != null) {
-      return implemented;
-    }
-
-    _implemented[type] = const {};
-    implemented = <Name, FunctionType>{};
-
-    var libraryUri = type.element.librarySource.uri;
-
-    void addMember(ExecutableElement member) {
-      if (!member.isAbstract && !member.isStatic) {
-        var name = new Name(libraryUri, member.name);
-        implemented[name] = member.type;
-      }
-    }
-
-    void addMembers(InterfaceType type) {
-      type.methods.forEach(addMember);
-      type.accessors.forEach(addMember);
-    }
-
-    if (type.superclass != null) {
-      var superImplemented = _getImplemented(type.superclass);
-      implemented.addAll(superImplemented);
-    }
-
-    // Mixins override the nominal superclass and previous mixins.
-    for (var mixin in type.mixins) {
-      var superImplemented = _getImplementedInMixin(mixin);
-      implemented.addAll(superImplemented);
-    }
-
-    // This type overrides everything from its actual superclass.
-    addMembers(type);
-
-    _implemented[type] = implemented;
-    return implemented;
-  }
-
-  /// TODO(scheglov) This repeats a lot of code from [_getImplemented].
-  Map<Name, FunctionType> _getImplementedInMixin(InterfaceType type) {
-    var implemented = _mixinMembers[type];
-    if (implemented != null) {
-      return implemented;
-    }
-
-    _mixinMembers[type] = const {};
-    implemented = <Name, FunctionType>{};
-
-    var libraryUri = type.element.librarySource.uri;
-
-    void addMember(ExecutableElement member) {
-      if (!member.isAbstract && !member.isStatic) {
-        var name = new Name(libraryUri, member.name);
-        implemented[name] = member.type;
-      }
-    }
-
-    void addMembers(InterfaceType type) {
-      type.methods.forEach(addMember);
-      type.accessors.forEach(addMember);
-    }
-
-    for (var mixin in type.mixins) {
-      var superImplemented = _getImplementedInMixin(mixin);
-      implemented.addAll(superImplemented);
-    }
-
-    // This type overrides everything from its actual superclass.
-    addMembers(type);
-
-    _mixinMembers[type] = implemented;
-    return implemented;
-  }
-
   Map<Name, FunctionType> _getTypeMembers(InterfaceType type) {
     var declared = <Name, FunctionType>{};
     var libraryUri = type.element.librarySource.uri;
@@ -402,14 +385,24 @@
 
     return declared;
   }
+
+  static bool _isDeclaredInObject(ExecutableElement element) {
+    var enclosing = element.enclosingElement;
+    return enclosing is ClassElement &&
+        enclosing.supertype == null &&
+        !enclosing.isMixin;
+  }
 }
 
 /// The instance interface of an [InterfaceType].
 class Interface {
-  static const _empty = const Interface._(
+  static final _empty = Interface._(
     const {},
     const {},
     const {},
+    Set<Name>(),
+    const {},
+    const {},
     const [{}],
     const [],
   );
@@ -420,6 +413,16 @@
   /// The map of declared names to their signatures.
   final Map<Name, FunctionType> declared;
 
+  /// The map of names to their concrete implementations.
+  final Map<Name, FunctionType> implemented;
+
+  /// The set of names that are `noSuchMethod` forwarders in [implemented].
+  final Set<Name> noSuchMethodForwarders;
+
+  /// The map of names to their concrete implementations that can be mixed
+  /// when this type is used as a mixin.
+  final Map<Name, FunctionType> implementedForMixing;
+
   /// The map of names to their signatures from the mixins, superclasses,
   /// or interfaces.
   final Map<Name, List<FunctionType>> _overridden;
@@ -435,9 +438,12 @@
   /// members of the class.
   final List<Conflict> conflicts;
 
-  const Interface._(
+  Interface._(
     this.map,
     this.declared,
+    this.implemented,
+    this.noSuchMethodForwarders,
+    this.implementedForMixing,
     this._overridden,
     this._superImplemented,
     this.conflicts,
diff --git a/pkg/analyzer/lib/src/error/inheritance_override.dart b/pkg/analyzer/lib/src/error/inheritance_override.dart
index 10c6bf8..20ecec0 100644
--- a/pkg/analyzer/lib/src/error/inheritance_override.dart
+++ b/pkg/analyzer/lib/src/error/inheritance_override.dart
@@ -122,6 +122,10 @@
       return;
     }
 
+    if (_checkForRecursiveInterfaceInheritance(classElement)) {
+      return;
+    }
+
     InterfaceTypeImpl type = classElement.type;
 
     // Add all superinterfaces of the direct supertype.
@@ -183,15 +187,13 @@
         }
 
         var interfaceType = interface.map[name];
-        var concreteType = inheritance.getMember(type, name, concrete: true);
+        var concreteType = interface.implemented[name];
 
         // No concrete implementation of the name.
         if (concreteType == null) {
-          if (!classElement.hasNoSuchMethod) {
-            if (!_reportConcreteClassWithAbstractMember(name.name)) {
-              inheritedAbstract ??= [];
-              inheritedAbstract.add(interfaceType);
-            }
+          if (!_reportConcreteClassWithAbstractMember(name.name)) {
+            inheritedAbstract ??= [];
+            inheritedAbstract.add(interfaceType);
           }
           continue;
         }
@@ -404,6 +406,106 @@
     }
   }
 
+  /// Check that [classElement] is not a superinterface to itself.
+  /// The [path] is a list containing the potentially cyclic implements path.
+  ///
+  /// See [CompileTimeErrorCode.RECURSIVE_INTERFACE_INHERITANCE],
+  /// [CompileTimeErrorCode.RECURSIVE_INTERFACE_INHERITANCE_EXTENDS],
+  /// [CompileTimeErrorCode.RECURSIVE_INTERFACE_INHERITANCE_IMPLEMENTS],
+  /// [CompileTimeErrorCode.RECURSIVE_INTERFACE_INHERITANCE_ON],
+  /// [CompileTimeErrorCode.RECURSIVE_INTERFACE_INHERITANCE_WITH].
+  bool _checkForRecursiveInterfaceInheritance(ClassElement element,
+      [List<ClassElement> path]) {
+    path ??= <ClassElement>[];
+
+    // Detect error condition.
+    int size = path.length;
+    // If this is not the base case (size > 0), and the enclosing class is the
+    // given class element then report an error.
+    if (size > 0 && classElement == element) {
+      String className = classElement.displayName;
+      if (size > 1) {
+        // Construct a string showing the cyclic implements path:
+        // "A, B, C, D, A"
+        String separator = ", ";
+        StringBuffer buffer = new StringBuffer();
+        for (int i = 0; i < size; i++) {
+          buffer.write(path[i].displayName);
+          buffer.write(separator);
+        }
+        buffer.write(element.displayName);
+        reporter.reportErrorForElement(
+            CompileTimeErrorCode.RECURSIVE_INTERFACE_INHERITANCE,
+            classElement,
+            [className, buffer.toString()]);
+        return true;
+      } else {
+        // RECURSIVE_INTERFACE_INHERITANCE_BASE_CASE_EXTENDS or
+        // RECURSIVE_INTERFACE_INHERITANCE_BASE_CASE_IMPLEMENTS or
+        // RECURSIVE_INTERFACE_INHERITANCE_ON or
+        // RECURSIVE_INTERFACE_INHERITANCE_BASE_CASE_WITH
+        reporter.reportErrorForElement(
+            _getRecursiveErrorCode(element), classElement, [className]);
+        return true;
+      }
+    }
+
+    if (path.indexOf(element) > 0) {
+      return false;
+    }
+    path.add(element);
+
+    // n-case
+    InterfaceType supertype = element.supertype;
+    if (supertype != null &&
+        _checkForRecursiveInterfaceInheritance(supertype.element, path)) {
+      return true;
+    }
+
+    for (InterfaceType type in element.mixins) {
+      if (_checkForRecursiveInterfaceInheritance(type.element, path)) {
+        return true;
+      }
+    }
+
+    for (InterfaceType type in element.superclassConstraints) {
+      if (_checkForRecursiveInterfaceInheritance(type.element, path)) {
+        return true;
+      }
+    }
+
+    for (InterfaceType type in element.interfaces) {
+      if (_checkForRecursiveInterfaceInheritance(type.element, path)) {
+        return true;
+      }
+    }
+
+    path.removeAt(path.length - 1);
+    return false;
+  }
+
+  /// Return the error code that should be used when the given class [element]
+  /// references itself directly.
+  ErrorCode _getRecursiveErrorCode(ClassElement element) {
+    if (element.supertype?.element == classElement) {
+      return CompileTimeErrorCode.RECURSIVE_INTERFACE_INHERITANCE_EXTENDS;
+    }
+
+    for (InterfaceType type in element.superclassConstraints) {
+      if (type.element == classElement) {
+        return CompileTimeErrorCode.RECURSIVE_INTERFACE_INHERITANCE_ON;
+      }
+    }
+
+    for (InterfaceType type in element.mixins) {
+      if (type.element == classElement) {
+        return CompileTimeErrorCode.RECURSIVE_INTERFACE_INHERITANCE_WITH;
+      }
+    }
+
+    return CompileTimeErrorCode.RECURSIVE_INTERFACE_INHERITANCE_IMPLEMENTS;
+  }
+
   /// We identified that the current non-abstract class does not have the
   /// concrete implementation of a method with the given [name].  If this is
   /// because the class itself defines an abstract method with this [name],
diff --git a/pkg/analyzer/lib/src/generated/error_verifier.dart b/pkg/analyzer/lib/src/generated/error_verifier.dart
index bff4d6b..baee3b51 100644
--- a/pkg/analyzer/lib/src/generated/error_verifier.dart
+++ b/pkg/analyzer/lib/src/generated/error_verifier.dart
@@ -1342,7 +1342,6 @@
         !_checkForAllMixinErrorCodes(withClause)) {
       _checkForImplicitDynamicType(superclass);
       _checkForExtendsDeferredClass(superclass);
-      _checkForRecursiveInterfaceInheritance(_enclosingClass);
       _checkForConflictingClassMembers();
       _checkForRepeatedType(implementsClause?.interfaces,
           CompileTimeErrorCode.IMPLEMENTS_REPEATED);
@@ -4168,11 +4167,6 @@
       return false;
     }
 
-    ClassElementImpl nominalSuperClass =
-        AbstractClassElementImpl.getImpl(_enclosingClass.supertype?.element);
-    bool nominalSuperClassHasNoSuchMethodForwarders =
-        nominalSuperClass != null && !nominalSuperClass.isAbstract;
-
     InterfaceTypeImpl enclosingType = _enclosingClass.type;
     Uri mixinLibraryUri = mixinElement.librarySource.uri;
     for (var name in mixinElementImpl.superInvokedNames) {
@@ -4183,9 +4177,6 @@
           forMixinIndex: mixinIndex, concrete: true, forSuper: true);
 
       if (superMemberType == null) {
-        if (nominalSuperClassHasNoSuchMethodForwarders) {
-          continue;
-        }
         _errorReporter.reportErrorForNode(
             CompileTimeErrorCode
                 .MIXIN_APPLICATION_NO_CONCRETE_SUPER_INVOKED_MEMBER,
@@ -4713,86 +4704,6 @@
   }
 
   /**
-   * Check that [_enclosingClass] is not a superinterface to itself.
-   *  The [path] is a list containing the potentially cyclic implements path.
-   *
-   * See [CompileTimeErrorCode.RECURSIVE_INTERFACE_INHERITANCE],
-   * [CompileTimeErrorCode.RECURSIVE_INTERFACE_INHERITANCE_EXTENDS],
-   * [CompileTimeErrorCode.RECURSIVE_INTERFACE_INHERITANCE_IMPLEMENTS],
-   * [CompileTimeErrorCode.RECURSIVE_INTERFACE_INHERITANCE_ON],
-   * [CompileTimeErrorCode.RECURSIVE_INTERFACE_INHERITANCE_WITH].
-   */
-  bool _checkForRecursiveInterfaceInheritance(ClassElement element,
-      [List<ClassElement> path]) {
-    path ??= <ClassElement>[];
-
-    // Detect error condition.
-    int size = path.length;
-    // If this is not the base case (size > 0), and the enclosing class is the
-    // given class element then an error an error.
-    if (size > 0 && _enclosingClass == element) {
-      String enclosingClassName = _enclosingClass.displayName;
-      if (size > 1) {
-        // Construct a string showing the cyclic implements path:
-        // "A, B, C, D, A"
-        String separator = ", ";
-        StringBuffer buffer = new StringBuffer();
-        for (int i = 0; i < size; i++) {
-          buffer.write(path[i].displayName);
-          buffer.write(separator);
-        }
-        buffer.write(element.displayName);
-        _errorReporter.reportErrorForElement(
-            CompileTimeErrorCode.RECURSIVE_INTERFACE_INHERITANCE,
-            _enclosingClass,
-            [enclosingClassName, buffer.toString()]);
-        return true;
-      } else {
-        // RECURSIVE_INTERFACE_INHERITANCE_BASE_CASE_EXTENDS or
-        // RECURSIVE_INTERFACE_INHERITANCE_BASE_CASE_IMPLEMENTS or
-        // RECURSIVE_INTERFACE_INHERITANCE_ON or
-        // RECURSIVE_INTERFACE_INHERITANCE_BASE_CASE_WITH
-        _errorReporter.reportErrorForElement(_getBaseCaseErrorCode(element),
-            _enclosingClass, [enclosingClassName]);
-        return true;
-      }
-    }
-
-    if (path.indexOf(element) > 0) {
-      return false;
-    }
-    path.add(element);
-
-    // n-case
-    InterfaceType supertype = element.supertype;
-    if (supertype != null &&
-        _checkForRecursiveInterfaceInheritance(supertype.element, path)) {
-      return true;
-    }
-
-    for (InterfaceType type in element.mixins) {
-      if (_checkForRecursiveInterfaceInheritance(type.element, path)) {
-        return true;
-      }
-    }
-
-    for (InterfaceType type in element.superclassConstraints) {
-      if (_checkForRecursiveInterfaceInheritance(type.element, path)) {
-        return true;
-      }
-    }
-
-    for (InterfaceType type in element.interfaces) {
-      if (_checkForRecursiveInterfaceInheritance(type.element, path)) {
-        return true;
-      }
-    }
-
-    path.removeAt(path.length - 1);
-    return false;
-  }
-
-  /**
    * Check that the given constructor [declaration] has a valid combination of
    * redirected constructor invocation(s), super constructor invocations and
    * field initializers.
@@ -5708,7 +5619,6 @@
     if (!_checkForOnClauseErrorCodes(onClause) &&
         !_checkForImplementsClauseErrorCodes(implementsClause)) {
 //      _checkForImplicitDynamicType(superclass);
-      _checkForRecursiveInterfaceInheritance(_enclosingClass);
       _checkForConflictingClassMembers();
       _checkForRepeatedType(
         onClause?.superclassConstraints,
@@ -5867,30 +5777,6 @@
   }
 
   /**
-   * Return the error code that should be used when the given class [element]
-   * references itself directly.
-   */
-  ErrorCode _getBaseCaseErrorCode(ClassElement element) {
-    if (element.supertype?.element == _enclosingClass) {
-      return CompileTimeErrorCode.RECURSIVE_INTERFACE_INHERITANCE_EXTENDS;
-    }
-
-    for (InterfaceType type in element.superclassConstraints) {
-      if (type.element == _enclosingClass) {
-        return CompileTimeErrorCode.RECURSIVE_INTERFACE_INHERITANCE_ON;
-      }
-    }
-
-    for (InterfaceType type in element.mixins) {
-      if (type.element == _enclosingClass) {
-        return CompileTimeErrorCode.RECURSIVE_INTERFACE_INHERITANCE_WITH;
-      }
-    }
-
-    return CompileTimeErrorCode.RECURSIVE_INTERFACE_INHERITANCE_IMPLEMENTS;
-  }
-
-  /**
    * Given an [expression] in a switch case whose value is expected to be an
    * enum constant, return the name of the constant.
    */
diff --git a/pkg/analyzer/lib/src/summary/link.dart b/pkg/analyzer/lib/src/summary/link.dart
index 5a9b656..b9b065e 100644
--- a/pkg/analyzer/lib/src/summary/link.dart
+++ b/pkg/analyzer/lib/src/summary/link.dart
@@ -700,6 +700,9 @@
       _unlinkedClass.interfaces.map(_computeInterfaceType).toList();
 
   @override
+  bool get isAbstract => _unlinkedClass.isAbstract;
+
+  @override
   bool get isEnum => false;
 
   @override
@@ -1011,6 +1014,9 @@
   bool get isMixin => false;
 
   @override
+  bool get isAbstract => false;
+
+  @override
   bool get isObject => false;
 
   @override
diff --git a/pkg/analyzer/test/src/dart/element/inheritance_manager2_test.dart b/pkg/analyzer/test/src/dart/element/inheritance_manager2_test.dart
index 4dddf19..60497d0 100644
--- a/pkg/analyzer/test/src/dart/element/inheritance_manager2_test.dart
+++ b/pkg/analyzer/test/src/dart/element/inheritance_manager2_test.dart
@@ -115,7 +115,7 @@
     );
   }
 
-  void test_getMember_() async {
+  test_getMember() async {
     addTestFile('''
 abstract class I1 {
   void f(int i);
@@ -136,7 +136,196 @@
     assertElementTypeString(memberType, '(Object) → void');
   }
 
-  test_preferLatest_mixin() async {
+  test_getMember_concrete() async {
+    addTestFile('''
+class A {
+  void foo() {}
+}
+''');
+    await resolveTestFile();
+
+    expect(
+      _getConcrete('A', 'foo'),
+      same(findElement.method('foo', of: 'A')),
+    );
+  }
+
+  test_getMember_concrete_abstract() async {
+    addTestFile('''
+abstract class A {
+  void foo();
+}
+''');
+    await resolveTestFile();
+
+    expect(_getConcrete('A', 'foo'), isNull);
+  }
+
+  test_getMember_concrete_fromMixedClass() async {
+    addTestFile('''
+class A {
+  void foo() {}
+}
+
+class X with A {}
+''');
+    await resolveTestFile();
+
+    expect(
+      _getConcrete('X', 'foo'),
+      same(findElement.method('foo', of: 'A')),
+    );
+  }
+
+  test_getMember_concrete_fromMixedClass2() async {
+    addTestFile('''
+class A {
+  void foo() {}
+}
+
+class B = Object with A;
+
+class X with B {}
+''');
+    await resolveTestFile();
+
+    expect(
+      _getConcrete('X', 'foo'),
+      same(findElement.method('foo', of: 'A')),
+    );
+  }
+
+  test_getMember_concrete_fromMixedClass_skipObject() async {
+    addTestFile('''
+class A {
+  String toString() => 'A';
+}
+
+class B {}
+
+class X extends A with B {}
+''');
+    await resolveTestFile();
+
+    expect(
+      _getConcrete('X', 'toString'),
+      same(findElement.method('toString', of: 'A')),
+    );
+  }
+
+  test_getMember_concrete_fromMixin() async {
+    addTestFile('''
+mixin M {
+  void foo() {}
+}
+
+class X with M {}
+''');
+    await resolveTestFile();
+
+    expect(
+      _getConcrete('X', 'foo'),
+      same(findElement.method('foo', of: 'M')),
+    );
+  }
+
+  test_getMember_concrete_fromSuper() async {
+    addTestFile('''
+class A {
+  void foo() {}
+}
+
+class B extends A {}
+
+abstract class C extends B {}
+''');
+    await resolveTestFile();
+
+    expect(
+      _getConcrete('B', 'foo'),
+      same(findElement.method('foo', of: 'A')),
+    );
+
+    expect(
+      _getConcrete('C', 'foo'),
+      same(findElement.method('foo', of: 'A')),
+    );
+  }
+
+  test_getMember_concrete_missing() async {
+    addTestFile('''
+abstract class A {}
+''');
+    await resolveTestFile();
+
+    expect(_getConcrete('A', 'foo'), isNull);
+  }
+
+  test_getMember_concrete_noSuchMethod() async {
+    addTestFile('''
+class A {
+  void foo() {}
+}
+
+class B implements A {
+  noSuchMethod(_) {}
+}
+
+abstract class C extends B {}
+''');
+    await resolveTestFile();
+
+    expect(
+      _getConcrete('B', 'foo'),
+      same(findElement.method('foo', of: 'A')),
+    );
+
+    expect(
+      _getConcrete('C', 'foo'),
+      same(findElement.method('foo', of: 'A')),
+    );
+  }
+
+  test_getMember_concrete_noSuchMethod_mixin() async {
+    addTestFile('''
+class A {
+  void foo();
+
+  noSuchMethod(_) {}
+}
+
+abstract class B extends Object with A {}
+''');
+    await resolveTestFile();
+
+    // noSuchMethod forwarders are not mixed-in.
+    // https://github.com/dart-lang/sdk/issues/33553#issuecomment-424638320
+    expect(_getConcrete('B', 'foo'), isNull);
+  }
+
+  test_getMember_concrete_noSuchMethod_moreSpecificSignature() async {
+    addTestFile('''
+class A {
+  void foo() {}
+}
+
+class B implements A {
+  noSuchMethod(_) {}
+}
+
+class C extends B {
+  void foo([a]);
+}
+''');
+    await resolveTestFile();
+
+    expect(
+      _getConcrete('C', 'foo'),
+      same(findElement.method('foo', of: 'C')),
+    );
+  }
+
+  test_getMember_preferLatest_mixin() async {
     addTestFile('''
 class A {
   void foo() {}
@@ -165,7 +354,7 @@
     expect(member.element, findElement.method('foo', of: 'M2'));
   }
 
-  test_preferLatest_superclass() async {
+  test_getMember_preferLatest_superclass() async {
     addTestFile('''
 class A {
   void foo() {}
@@ -190,7 +379,7 @@
     expect(member.element, findElement.method('foo', of: 'B'));
   }
 
-  test_preferLatest_this() async {
+  test_getMember_preferLatest_this() async {
     addTestFile('''
 class A {
   void foo() {}
@@ -213,8 +402,103 @@
     expect(member.element, findElement.method('foo', of: 'X'));
   }
 
+  test_getMember_super_abstract() async {
+    addTestFile('''
+abstract class A {
+  void foo();
+}
+
+class B extends A {
+  noSuchMethod(_) {}
+}
+''');
+    await resolveTestFile();
+
+    expect(_getSuper('B', 'foo'), isNull);
+  }
+
+  test_getMember_super_fromMixin() async {
+    addTestFile('''
+mixin M {
+  void foo() {}
+}
+
+class X extends Object with M {
+  void foo() {}
+}
+''');
+    await resolveTestFile();
+
+    expect(
+      _getSuper('X', 'foo'),
+      same(findElement.method('foo', of: 'M')),
+    );
+  }
+
+  test_getMember_super_fromSuper() async {
+    addTestFile('''
+class A {
+  void foo() {}
+}
+
+class B extends A {
+  void foo() {}
+}
+''');
+    await resolveTestFile();
+
+    expect(
+      _getSuper('B', 'foo'),
+      same(findElement.method('foo', of: 'A')),
+    );
+  }
+
+  test_getMember_super_missing() async {
+    addTestFile('''
+class A {}
+
+class B extends A {}
+''');
+    await resolveTestFile();
+
+    expect(_getSuper('B', 'foo'), isNull);
+  }
+
+  test_getMember_super_noSuchMember() async {
+    addTestFile('''
+class A {
+  void foo();
+  noSuchMethod(_) {}
+}
+
+class B extends A {
+  void foo() {}
+}
+''');
+    await resolveTestFile();
+
+    expect(
+      _getSuper('B', 'foo'),
+      same(findElement.method('foo', of: 'A')),
+    );
+  }
+
+  ExecutableElement _getConcrete(String className, String name) {
+    var type = findElement.class_(className).type;
+    return manager
+        .getMember(type, new Name(null, name), concrete: true)
+        ?.element;
+  }
+
   ExecutableElement _getInherited(String className, String name) {
     var type = findElement.class_(className).type;
     return manager.getInherited(type, new Name(null, name)).element;
   }
+
+  ExecutableElement _getSuper(String className, String name) {
+    var type = findElement.class_(className).type;
+    return manager
+        .getMember(type, new Name(null, name), forSuper: true)
+        ?.element;
+  }
 }
diff --git a/pkg/analyzer/test/src/dart/resolution/class_test.dart b/pkg/analyzer/test/src/dart/resolution/class_test.dart
index 5650cdb..efcec88 100644
--- a/pkg/analyzer/test/src/dart/resolution/class_test.dart
+++ b/pkg/analyzer/test/src/dart/resolution/class_test.dart
@@ -1511,8 +1511,6 @@
     assertTestErrors([
       CompileTimeErrorCode.RECURSIVE_INTERFACE_INHERITANCE,
       CompileTimeErrorCode.RECURSIVE_INTERFACE_INHERITANCE,
-      StaticWarningCode.NON_ABSTRACT_CLASS_INHERITS_ABSTRACT_MEMBER_FIVE_PLUS,
-      StaticWarningCode.NON_ABSTRACT_CLASS_INHERITS_ABSTRACT_MEMBER_FIVE_PLUS,
     ]);
   }
 
@@ -1568,7 +1566,6 @@
     await resolveTestFile();
     assertTestErrors([
       CompileTimeErrorCode.RECURSIVE_INTERFACE_INHERITANCE_EXTENDS,
-      StaticWarningCode.CONCRETE_CLASS_WITH_ABSTRACT_MEMBER,
     ]);
   }
 
diff --git a/pkg/analyzer/test/src/dart/resolution/mixin_test.dart b/pkg/analyzer/test/src/dart/resolution/mixin_test.dart
index bc792f0..65cb6d7 100644
--- a/pkg/analyzer/test/src/dart/resolution/mixin_test.dart
+++ b/pkg/analyzer/test/src/dart/resolution/mixin_test.dart
@@ -1026,6 +1026,32 @@
     assertNoTestErrors();
   }
 
+  test_error_mixinApplicationNoConcreteSuperInvokedMember_OK_hasNSM2() async {
+    addTestFile(r'''
+abstract class A {
+  void foo();
+}
+
+mixin M on A {
+  void bar() {
+    super.foo();
+  }
+}
+
+/// Class `B` has noSuchMethod forwarder for `foo`.
+class B implements A {
+  noSuchMethod(_) {}
+}
+
+/// Class `C` is abstract, but it inherits noSuchMethod forwarders from `B`.
+abstract class C extends B {}
+
+class X extends C with M {}
+''');
+    await resolveTestFile();
+    assertNoTestErrors();
+  }
+
   test_error_mixinApplicationNoConcreteSuperInvokedMember_OK_inPreviousMixin() async {
     addTestFile(r'''
 abstract class A {