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 {