Support mixins in more places in index and search.
R=brianwilkerson@google.com
Change-Id: I523726e1c7f5efa2d6e2f4b91204da3f14f181b6
Reviewed-on: https://dart-review.googlesource.com/75120
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
diff --git a/pkg/analysis_server/lib/src/services/search/hierarchy.dart b/pkg/analysis_server/lib/src/services/search/hierarchy.dart
index 909f3e8..8862eda 100644
--- a/pkg/analysis_server/lib/src/services/search/hierarchy.dart
+++ b/pkg/analysis_server/lib/src/services/search/hierarchy.dart
@@ -173,6 +173,10 @@
queue.add(superType.element);
}
}
+ // append superclass constraints
+ for (InterfaceType interface in current.superclassConstraints) {
+ queue.add(interface.element);
+ }
// append interfaces
for (InterfaceType interface in current.interfaces) {
queue.add(interface.element);
diff --git a/pkg/analysis_server/lib/src/status/element_writer.dart b/pkg/analysis_server/lib/src/status/element_writer.dart
index fcdc776..3b6ac79 100644
--- a/pkg/analysis_server/lib/src/status/element_writer.dart
+++ b/pkg/analysis_server/lib/src/status/element_writer.dart
@@ -54,6 +54,7 @@
properties['isProxy'] = element.isProxy;
properties['isValidMixin'] = element.isValidMixin;
properties['mixins'] = element.mixins;
+ properties['superclassConstraints'] = element.superclassConstraints;
properties['supertype'] = element.supertype;
}
if (element is ClassMemberElement) {
diff --git a/pkg/analysis_server/test/search/type_hierarchy_test.dart b/pkg/analysis_server/test/search/type_hierarchy_test.dart
index f993d47..ee2f6b5 100644
--- a/pkg/analysis_server/test/search/type_hierarchy_test.dart
+++ b/pkg/analysis_server/test/search/type_hierarchy_test.dart
@@ -887,6 +887,63 @@
expect(member2.location.offset, findOffset('test(x) {} // in Derived2'));
}
+ Future<void> test_member_ofSuperclassConstraint_getter() async {
+ addTestFile('''
+class A {
+ get test => 0; // in A
+}
+
+mixin M on A {
+ get test => 0; // in M
+}
+''');
+ var items = await _getTypeHierarchy('test => 0; // in A');
+
+ var inA = items.firstWhere((e) => e.classElement.name == 'A');
+ var inM = items.firstWhere((e) => e.classElement.name == 'M');
+
+ _assertMember(inA, 'test => 0; // in A');
+ _assertMember(inM, 'test => 0; // in M');
+ }
+
+ Future<void> test_member_ofSuperclassConstraint_method() async {
+ addTestFile('''
+class A {
+ void test() {} // in A
+}
+
+mixin M on A {
+ void test() {} // in M
+}
+''');
+ var items = await _getTypeHierarchy('test() {} // in A');
+
+ var inA = items.firstWhere((e) => e.classElement.name == 'A');
+ var inM = items.firstWhere((e) => e.classElement.name == 'M');
+
+ _assertMember(inA, 'test() {} // in A');
+ _assertMember(inM, 'test() {} // in M');
+ }
+
+ Future<void> test_member_ofSuperclassConstraint_setter() async {
+ addTestFile('''
+class A {
+ set test(x) {} // in A
+}
+
+mixin M on A {
+ set test(x) {} // in M
+}
+''');
+ var items = await _getTypeHierarchy('test(x) {} // in A');
+
+ var inA = items.firstWhere((e) => e.classElement.name == 'A');
+ var inM = items.firstWhere((e) => e.classElement.name == 'M');
+
+ _assertMember(inA, 'test(x) {} // in A');
+ _assertMember(inM, 'test(x) {} // in M');
+ }
+
Future<void> test_member_operator() async {
addTestFile('''
class A {
@@ -1024,6 +1081,10 @@
expect(items, isNull);
}
+ void _assertMember(TypeHierarchyItem item, String search) {
+ expect(item.memberElement.location.offset, findOffset(search));
+ }
+
Request _createGetTypeHierarchyRequest(String search, {bool superOnly}) {
return new SearchGetTypeHierarchyParams(testFile, findOffset(search),
superOnly: superOnly)
diff --git a/pkg/analysis_server/test/services/search/hierarchy_test.dart b/pkg/analysis_server/test/services/search/hierarchy_test.dart
index b824347..cec129e4 100644
--- a/pkg/analysis_server/test/services/search/hierarchy_test.dart
+++ b/pkg/analysis_server/test/services/search/hierarchy_test.dart
@@ -445,6 +445,45 @@
}
}
+ test_getSuperClasses_superclassConstraints() async {
+ await _indexTestUnit('''
+class A {}
+class B extends A {}
+class C {}
+
+mixin M1 on A {}
+mixin M2 on B {}
+mixin M3 on M1 {}
+mixin M4 on M2 {}
+mixin M5 on A, C {}
+''');
+ ClassElement a = findElement('A');
+ ClassElement b = findElement('B');
+ ClassElement c = findElement('C');
+ ClassElement m1 = findElement('M1');
+ ClassElement m2 = findElement('M2');
+ ClassElement m3 = findElement('M3');
+ ClassElement m4 = findElement('M4');
+ ClassElement m5 = findElement('M5');
+ ClassElement object = a.supertype.element;
+
+ _assertSuperClasses(object, []);
+ _assertSuperClasses(a, [object]);
+ _assertSuperClasses(b, [object, a]);
+ _assertSuperClasses(c, [object]);
+
+ _assertSuperClasses(m1, [object, a]);
+ _assertSuperClasses(m2, [object, a, b]);
+ _assertSuperClasses(m3, [object, a, m1]);
+ _assertSuperClasses(m4, [object, a, b, m2]);
+ _assertSuperClasses(m5, [object, a, c]);
+ }
+
+ void _assertSuperClasses(ClassElement element, List<ClassElement> expected) {
+ var supers = getSuperClasses(element);
+ expect(supers, unorderedEquals(expected));
+ }
+
Future<void> _indexTestUnit(String code) async {
await resolveTestUnit(code);
}
diff --git a/pkg/analyzer/lib/src/dart/analysis/index.dart b/pkg/analyzer/lib/src/dart/analysis/index.dart
index 1afbb1d..69dd439 100644
--- a/pkg/analyzer/lib/src/dart/analysis/index.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/index.dart
@@ -627,6 +627,13 @@
}
@override
+ visitMixinDeclaration(MixinDeclaration node) {
+ _addSubtypeForMixinDeclaration(node);
+ recordIsAncestorOf(node.declaredElement);
+ super.visitMixinDeclaration(node);
+ }
+
+ @override
visitOnClause(OnClause node) {
for (TypeName typeName in node.superclassConstraints) {
recordSuperType(typeName, IndexRelationKind.IS_IMPLEMENTED_BY);
@@ -747,8 +754,12 @@
/**
* Record the given class as a subclass of its direct superclasses.
*/
- void _addSubtype(String name, TypeName superclass, WithClause withClause,
- ImplementsClause implementsClause, List<ClassMember> memberNodes) {
+ void _addSubtype(String name,
+ {TypeName superclass,
+ WithClause withClause,
+ OnClause onClause,
+ ImplementsClause implementsClause,
+ List<ClassMember> memberNodes}) {
List<String> supertypes = [];
List<String> members = [];
@@ -770,6 +781,7 @@
addSupertype(superclass);
withClause?.mixinTypes?.forEach(addSupertype);
+ onClause?.superclassConstraints?.forEach(addSupertype);
implementsClause?.interfaces?.forEach(addSupertype);
void addMemberName(SimpleIdentifier identifier) {
@@ -802,16 +814,32 @@
* Record the given class as a subclass of its direct superclasses.
*/
void _addSubtypeForClassDeclaration(ClassDeclaration node) {
- _addSubtype(node.name.name, node.extendsClause?.superclass, node.withClause,
- node.implementsClause, node.members);
+ _addSubtype(node.name.name,
+ superclass: node.extendsClause?.superclass,
+ withClause: node.withClause,
+ implementsClause: node.implementsClause,
+ memberNodes: node.members);
}
/**
* Record the given class as a subclass of its direct superclasses.
*/
void _addSubtypeForClassTypeAlis(ClassTypeAlias node) {
- _addSubtype(node.name.name, node.superclass, node.withClause,
- node.implementsClause, const []);
+ _addSubtype(node.name.name,
+ superclass: node.superclass,
+ withClause: node.withClause,
+ implementsClause: node.implementsClause,
+ memberNodes: const []);
+ }
+
+ /**
+ * Record the given mixin as a subclass of its direct superclasses.
+ */
+ void _addSubtypeForMixinDeclaration(MixinDeclaration node) {
+ _addSubtype(node.name.name,
+ onClause: node.onClause,
+ implementsClause: node.implementsClause,
+ memberNodes: node.members);
}
/**
@@ -871,6 +899,9 @@
for (InterfaceType mixinType in ancestor.mixins) {
_recordIsAncestorOf(descendant, mixinType.element, true, visitedElements);
}
+ for (InterfaceType type in ancestor.superclassConstraints) {
+ _recordIsAncestorOf(descendant, type.element, true, visitedElements);
+ }
for (InterfaceType implementedType in ancestor.interfaces) {
_recordIsAncestorOf(
descendant, implementedType.element, true, visitedElements);
diff --git a/pkg/analyzer/lib/src/dart/element/type.dart b/pkg/analyzer/lib/src/dart/element/type.dart
index 7259bf9..e19757b 100644
--- a/pkg/analyzer/lib/src/dart/element/type.dart
+++ b/pkg/analyzer/lib/src/dart/element/type.dart
@@ -1511,6 +1511,15 @@
return true;
}
//
+ // I is listed in the on clause of J.
+ //
+ for (InterfaceType interfaceType in jElement.superclassConstraints) {
+ interfaceType = interfaceType.substitute2(jArgs, jVars);
+ if (interfaceType == i) {
+ return true;
+ }
+ }
+ //
// I is listed in the implements clause of J.
//
for (InterfaceType interfaceType in jElement.interfaces) {
@@ -2209,19 +2218,28 @@
int longestPath = 1;
try {
visitedTypes.add(classElement);
- List<InterfaceType> superinterfaces = classElement.interfaces;
int pathLength;
- if (superinterfaces.length > 0) {
- // loop through each of the superinterfaces recursively calling this
- // method and keeping track of the longest path to return
- for (InterfaceType superinterface in superinterfaces) {
- pathLength = _computeLongestInheritancePathToObject(
- superinterface, depth + 1, visitedTypes);
- if (pathLength > longestPath) {
- longestPath = pathLength;
- }
+
+ // loop through each of the superinterfaces recursively calling this
+ // method and keeping track of the longest path to return
+ for (InterfaceType interface in classElement.superclassConstraints) {
+ pathLength = _computeLongestInheritancePathToObject(
+ interface, depth + 1, visitedTypes);
+ if (pathLength > longestPath) {
+ longestPath = pathLength;
}
}
+
+ // loop through each of the superinterfaces recursively calling this
+ // method and keeping track of the longest path to return
+ for (InterfaceType interface in classElement.interfaces) {
+ pathLength = _computeLongestInheritancePathToObject(
+ interface, depth + 1, visitedTypes);
+ if (pathLength > longestPath) {
+ longestPath = pathLength;
+ }
+ }
+
// finally, perform this same check on the super type
// TODO(brianwilkerson) Does this also need to add in the number of mixin
// classes?
diff --git a/pkg/analyzer/test/src/dart/analysis/index_test.dart b/pkg/analyzer/test/src/dart/analysis/index_test.dart
index 9a28fc6..179e4c4 100644
--- a/pkg/analyzer/test/src/dart/analysis/index_test.dart
+++ b/pkg/analyzer/test/src/dart/analysis/index_test.dart
@@ -101,6 +101,27 @@
assertThat(classElementB)..isAncestorOf('C2 = Object with B');
}
+ test_hasAncestor_MixinDeclaration() async {
+ await _indexTestUnit('''
+class A {}
+class B extends A {}
+
+mixin M1 on A {}
+mixin M2 on B {}
+mixin M3 implements A {}
+mixin M4 implements B {}
+mixin M5 on M2 {}
+''');
+ ClassElement classElementA = findElement('A');
+ assertThat(classElementA)
+ ..isAncestorOf('B extends A')
+ ..isAncestorOf('M1 on A')
+ ..isAncestorOf('M2 on B')
+ ..isAncestorOf('M3 implements A')
+ ..isAncestorOf('M4 implements B')
+ ..isAncestorOf('M5 on M2');
+ }
+
test_isExtendedBy_ClassDeclaration() async {
await _indexTestUnit('''
class A {} // 1
@@ -1058,6 +1079,35 @@
expect(X.members, ['foo']);
}
+ test_subtypes_mixinDeclaration() async {
+ String libP = 'package:test/lib.dart;package:test/lib.dart';
+ provider.newFile(_p('$testProject/lib.dart'), '''
+class A {}
+class B {}
+class C {}
+class D {}
+class E {}
+''');
+ await _indexTestUnit('''
+import 'lib.dart';
+
+mixin X on A implements B, C {}
+mixin Y on A, B implements C;
+''');
+
+ {
+ var X = index.subtypes.singleWhere((t) => t.name == 'X');
+ expect(X.supertypes, ['$libP;A', '$libP;B', '$libP;C']);
+ expect(X.members, isEmpty);
+ }
+
+ {
+ var Y = index.subtypes.singleWhere((t) => t.name == 'Y');
+ expect(Y.supertypes, ['$libP;A', '$libP;B', '$libP;C']);
+ expect(Y.members, isEmpty);
+ }
+ }
+
test_usedName_inLibraryIdentifier() async {
await _indexTestUnit('''
library aaa.bbb.ccc;