Issue 35127. Fixes for mixin implements / overrides.

R=brianwilkerson@google.com

Bug: https://github.com/dart-lang/sdk/issues/35127
Change-Id: I5869175cee13b6e22ce25c2d361b0b1aa52efe48
Reviewed-on: https://dart-review.googlesource.com/c/91690
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
This commit is contained in:
Konstantin Shcheglov 2019-01-31 16:01:55 +00:00 committed by commit-bot@chromium.org
parent fdd477721d
commit e5164be27e
7 changed files with 159 additions and 22 deletions

View file

@ -32,7 +32,7 @@ class DartUnitOverridesComputer {
*/
List<proto.Override> compute() {
for (CompilationUnitMember unitMember in _unit.declarations) {
if (unitMember is ClassDeclaration) {
if (unitMember is ClassOrMixinDeclaration) {
for (ClassMember classMember in unitMember.members) {
if (classMember is MethodDeclaration) {
if (classMember.isStatic) {
@ -198,6 +198,7 @@ class _OverriddenElementsFinder {
_addSuperOverrides(type.superclass);
type.mixins.forEach(_addSuperOverrides);
type.superclassConstraints.forEach(_addSuperOverrides);
}
Element _lookupMember(ClassElement classElement) {

View file

@ -18,26 +18,11 @@ class ImplementedComputer {
ImplementedComputer(this.searchEngine, this.unitElement);
compute() async {
// TODO(brianwilkerson) Determine whether this await is necessary.
await null;
for (ClassElement type in unitElement.types) {
// Always include Object and its members.
if (type.supertype == null) {
_addImplementedClass(type);
type.accessors.forEach(_addImplementedMember);
type.fields.forEach(_addImplementedMember);
type.methods.forEach(_addImplementedMember);
continue;
}
// Analyze subtypes.
subtypeMembers = await searchEngine.membersOfSubtypes(type);
if (subtypeMembers != null) {
_addImplementedClass(type);
type.accessors.forEach(_addMemberIfImplemented);
type.fields.forEach(_addMemberIfImplemented);
type.methods.forEach(_addMemberIfImplemented);
}
for (var element in unitElement.mixins) {
await _computeForClassElement(element);
}
for (var element in unitElement.types) {
await _computeForClassElement(element);
}
}
@ -62,6 +47,26 @@ class ImplementedComputer {
}
}
Future<void> _computeForClassElement(ClassElement element) async {
// Always include Object and its members.
if (element.supertype == null && !element.isMixin) {
_addImplementedClass(element);
element.accessors.forEach(_addImplementedMember);
element.fields.forEach(_addImplementedMember);
element.methods.forEach(_addImplementedMember);
return;
}
// Analyze subtypes.
subtypeMembers = await searchEngine.membersOfSubtypes(element);
if (subtypeMembers != null) {
_addImplementedClass(element);
element.accessors.forEach(_addMemberIfImplemented);
element.fields.forEach(_addMemberIfImplemented);
element.methods.forEach(_addMemberIfImplemented);
}
}
bool _hasOverride(Element element) {
String name = element.displayName;
return subtypeMembers.contains(name);

View file

@ -71,7 +71,7 @@ class AnalysisNotificationImplementedTest extends AbstractAnalysisTest {
}
/**
* Validates that there is no an [ImplementedClass] at the offset of [search].
* Validates that there is no [ImplementedMember] at the offset of [search].
*
* If [length] is not specified explicitly, then length of an identifier
* from [search] is used.
@ -167,6 +167,21 @@ class B implements A {}
assertHasImplementedClass('A {');
}
test_class_inMixin() async {
addTestFile('''
class A {} // ref
class B {} // ref
class C {} // ref
class D {} // ref
mixin M on A, B implements C, D {}
''');
await prepareImplementedElements();
assertHasImplementedClass('A {} // ref');
assertHasImplementedClass('B {} // ref');
assertHasImplementedClass('C {} // ref');
assertHasImplementedClass('D {} // ref');
}
test_class_mixed() async {
addTestFile('''
class A {}
@ -313,6 +328,40 @@ class B extends A {
assertHasImplementedMember('m(); // A');
}
test_mixin_implemented() async {
addTestFile('''
mixin M { // ref
void foo() {} // ref
void bar() {} // ref
}
class A implements M {
void foo() {}
}
''');
await prepareImplementedElements();
assertHasImplementedClass('M { // ref');
assertHasImplementedMember('foo() {} // ref');
assertNoImplementedMember('bar() {} // ref');
}
test_mixin_mixed() async {
addTestFile('''
mixin M { // ref
void foo() {} // ref
void bar() {} // ref
}
class A extends Object with M {
void foo() {}
}
''');
await prepareImplementedElements();
assertHasImplementedClass('M { // ref');
assertHasImplementedMember('foo() {} // ref');
assertNoImplementedMember('bar() {} // ref');
}
test_setter_withField() async {
addTestFile('''
class A {

View file

@ -305,6 +305,38 @@ class C extends B {
assertHasInterfaceMember('m() {} // in A');
}
test_inMixin_interface_method_direct_single() async {
addTestFile('''
class A {
m() {} // in A
}
mixin M implements A {
m() {} // in M
}
''');
await prepareOverrides();
assertHasOverride('m() {} // in M');
assertNoSuperMember();
assertHasInterfaceMember('m() {} // in A');
}
test_inMixin_superclassConstraint_method_direct() async {
addTestFile('''
class A {
m() {} // in A
}
mixin M on A {
m() {} // in M
}
''');
await prepareOverrides();
assertHasOverride('m() {} // in M');
assertHasSuperElement('m() {} // in A');
assertNoInterfaceMembers();
}
test_interface_method_direct_multiple() async {
addTestFile('''
class IA {

View file

@ -45,6 +45,9 @@ Set<String> computeSubtypedNames(CompilationUnit unit) {
_addSubtypedName(declaration.superclass);
_addSubtypedNames(declaration.withClause?.mixinTypes);
_addSubtypedNames(declaration.implementsClause?.interfaces);
} else if (declaration is MixinDeclaration) {
_addSubtypedNames(declaration.onClause?.superclassConstraints);
_addSubtypedNames(declaration.implementsClause?.interfaces);
}
}

View file

@ -431,6 +431,14 @@ class X = A with B implements C, D, E;
expect(names, unorderedEquals(['A', 'B', 'C', 'D', 'E']));
}
void test_mixinDeclaration() {
Set<String> names = _computeSubtypedNames('''
import 'lib.dart';
mixin M on A, B implements C, D {}
''');
expect(names, unorderedEquals(['A', 'B', 'C', 'D']));
}
void test_prefixed() {
Set<String> names = _computeSubtypedNames('''
import 'lib.dart' as p;

View file

@ -1767,6 +1767,45 @@ class F {}
}
}
test_subtypes_mixin_superclassConstraints() async {
await _resolveTestUnit('''
class A {
void methodA() {}
}
class B {
void methodB() {}
}
mixin M on A, B {
void methodA() {}
void methodM() {}
}
''');
ClassElement a = _findElement('A');
ClassElement b = _findElement('B');
{
var subtypes = await driver.search.subtypes(type: a);
expect(subtypes, hasLength(1));
var m = subtypes.singleWhere((r) => r.name == 'M');
expect(m.libraryUri, testUri);
expect(m.id, '$testUri;$testUri;M');
expect(m.members, ['methodA', 'methodM']);
}
{
var subtypes = await driver.search.subtypes(type: b);
expect(subtypes, hasLength(1));
var m = subtypes.singleWhere((r) => r.name == 'M');
expect(m.libraryUri, testUri);
expect(m.id, '$testUri;$testUri;M');
expect(m.members, ['methodA', 'methodM']);
}
}
test_subtypes_discover() async {
var pathT = convertPath('/test/lib/t.dart');
var pathA = convertPath('/aaa/lib/a.dart');