Skip to content

Commit c5d77d1

Browse files
scheglovcommit-bot@chromium.org
authored andcommitted
Support mixins in more places in index and search.
[email protected] Change-Id: I523726e1c7f5efa2d6e2f4b91204da3f14f181b6 Reviewed-on: https://dart-review.googlesource.com/75120 Reviewed-by: Brian Wilkerson <[email protected]> Commit-Queue: Konstantin Shcheglov <[email protected]>
1 parent c3bdb6c commit c5d77d1

File tree

7 files changed

+220
-16
lines changed

7 files changed

+220
-16
lines changed

pkg/analysis_server/lib/src/services/search/hierarchy.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,10 @@ Set<ClassElement> getSuperClasses(ClassElement seed) {
173173
queue.add(superType.element);
174174
}
175175
}
176+
// append superclass constraints
177+
for (InterfaceType interface in current.superclassConstraints) {
178+
queue.add(interface.element);
179+
}
176180
// append interfaces
177181
for (InterfaceType interface in current.interfaces) {
178182
queue.add(interface.element);

pkg/analysis_server/lib/src/status/element_writer.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ class ElementWriter extends GeneralizingElementVisitor with TreeWriter {
5454
properties['isProxy'] = element.isProxy;
5555
properties['isValidMixin'] = element.isValidMixin;
5656
properties['mixins'] = element.mixins;
57+
properties['superclassConstraints'] = element.superclassConstraints;
5758
properties['supertype'] = element.supertype;
5859
}
5960
if (element is ClassMemberElement) {

pkg/analysis_server/test/search/type_hierarchy_test.dart

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -887,6 +887,63 @@ class Derived2 extends Base {
887887
expect(member2.location.offset, findOffset('test(x) {} // in Derived2'));
888888
}
889889

890+
Future<void> test_member_ofSuperclassConstraint_getter() async {
891+
addTestFile('''
892+
class A {
893+
get test => 0; // in A
894+
}
895+
896+
mixin M on A {
897+
get test => 0; // in M
898+
}
899+
''');
900+
var items = await _getTypeHierarchy('test => 0; // in A');
901+
902+
var inA = items.firstWhere((e) => e.classElement.name == 'A');
903+
var inM = items.firstWhere((e) => e.classElement.name == 'M');
904+
905+
_assertMember(inA, 'test => 0; // in A');
906+
_assertMember(inM, 'test => 0; // in M');
907+
}
908+
909+
Future<void> test_member_ofSuperclassConstraint_method() async {
910+
addTestFile('''
911+
class A {
912+
void test() {} // in A
913+
}
914+
915+
mixin M on A {
916+
void test() {} // in M
917+
}
918+
''');
919+
var items = await _getTypeHierarchy('test() {} // in A');
920+
921+
var inA = items.firstWhere((e) => e.classElement.name == 'A');
922+
var inM = items.firstWhere((e) => e.classElement.name == 'M');
923+
924+
_assertMember(inA, 'test() {} // in A');
925+
_assertMember(inM, 'test() {} // in M');
926+
}
927+
928+
Future<void> test_member_ofSuperclassConstraint_setter() async {
929+
addTestFile('''
930+
class A {
931+
set test(x) {} // in A
932+
}
933+
934+
mixin M on A {
935+
set test(x) {} // in M
936+
}
937+
''');
938+
var items = await _getTypeHierarchy('test(x) {} // in A');
939+
940+
var inA = items.firstWhere((e) => e.classElement.name == 'A');
941+
var inM = items.firstWhere((e) => e.classElement.name == 'M');
942+
943+
_assertMember(inA, 'test(x) {} // in A');
944+
_assertMember(inM, 'test(x) {} // in M');
945+
}
946+
890947
Future<void> test_member_operator() async {
891948
addTestFile('''
892949
class A {
@@ -1024,6 +1081,10 @@ class D extends C {}
10241081
expect(items, isNull);
10251082
}
10261083

1084+
void _assertMember(TypeHierarchyItem item, String search) {
1085+
expect(item.memberElement.location.offset, findOffset(search));
1086+
}
1087+
10271088
Request _createGetTypeHierarchyRequest(String search, {bool superOnly}) {
10281089
return new SearchGetTypeHierarchyParams(testFile, findOffset(search),
10291090
superOnly: superOnly)

pkg/analysis_server/test/services/search/hierarchy_test.dart

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -445,6 +445,45 @@ class F implements A {}
445445
}
446446
}
447447

448+
test_getSuperClasses_superclassConstraints() async {
449+
await _indexTestUnit('''
450+
class A {}
451+
class B extends A {}
452+
class C {}
453+
454+
mixin M1 on A {}
455+
mixin M2 on B {}
456+
mixin M3 on M1 {}
457+
mixin M4 on M2 {}
458+
mixin M5 on A, C {}
459+
''');
460+
ClassElement a = findElement('A');
461+
ClassElement b = findElement('B');
462+
ClassElement c = findElement('C');
463+
ClassElement m1 = findElement('M1');
464+
ClassElement m2 = findElement('M2');
465+
ClassElement m3 = findElement('M3');
466+
ClassElement m4 = findElement('M4');
467+
ClassElement m5 = findElement('M5');
468+
ClassElement object = a.supertype.element;
469+
470+
_assertSuperClasses(object, []);
471+
_assertSuperClasses(a, [object]);
472+
_assertSuperClasses(b, [object, a]);
473+
_assertSuperClasses(c, [object]);
474+
475+
_assertSuperClasses(m1, [object, a]);
476+
_assertSuperClasses(m2, [object, a, b]);
477+
_assertSuperClasses(m3, [object, a, m1]);
478+
_assertSuperClasses(m4, [object, a, b, m2]);
479+
_assertSuperClasses(m5, [object, a, c]);
480+
}
481+
482+
void _assertSuperClasses(ClassElement element, List<ClassElement> expected) {
483+
var supers = getSuperClasses(element);
484+
expect(supers, unorderedEquals(expected));
485+
}
486+
448487
Future<void> _indexTestUnit(String code) async {
449488
await resolveTestUnit(code);
450489
}

pkg/analyzer/lib/src/dart/analysis/index.dart

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -626,6 +626,13 @@ class _IndexContributor extends GeneralizingAstVisitor {
626626
node.argumentList?.accept(this);
627627
}
628628

629+
@override
630+
visitMixinDeclaration(MixinDeclaration node) {
631+
_addSubtypeForMixinDeclaration(node);
632+
recordIsAncestorOf(node.declaredElement);
633+
super.visitMixinDeclaration(node);
634+
}
635+
629636
@override
630637
visitOnClause(OnClause node) {
631638
for (TypeName typeName in node.superclassConstraints) {
@@ -747,8 +754,12 @@ class _IndexContributor extends GeneralizingAstVisitor {
747754
/**
748755
* Record the given class as a subclass of its direct superclasses.
749756
*/
750-
void _addSubtype(String name, TypeName superclass, WithClause withClause,
751-
ImplementsClause implementsClause, List<ClassMember> memberNodes) {
757+
void _addSubtype(String name,
758+
{TypeName superclass,
759+
WithClause withClause,
760+
OnClause onClause,
761+
ImplementsClause implementsClause,
762+
List<ClassMember> memberNodes}) {
752763
List<String> supertypes = [];
753764
List<String> members = [];
754765

@@ -770,6 +781,7 @@ class _IndexContributor extends GeneralizingAstVisitor {
770781

771782
addSupertype(superclass);
772783
withClause?.mixinTypes?.forEach(addSupertype);
784+
onClause?.superclassConstraints?.forEach(addSupertype);
773785
implementsClause?.interfaces?.forEach(addSupertype);
774786

775787
void addMemberName(SimpleIdentifier identifier) {
@@ -802,16 +814,32 @@ class _IndexContributor extends GeneralizingAstVisitor {
802814
* Record the given class as a subclass of its direct superclasses.
803815
*/
804816
void _addSubtypeForClassDeclaration(ClassDeclaration node) {
805-
_addSubtype(node.name.name, node.extendsClause?.superclass, node.withClause,
806-
node.implementsClause, node.members);
817+
_addSubtype(node.name.name,
818+
superclass: node.extendsClause?.superclass,
819+
withClause: node.withClause,
820+
implementsClause: node.implementsClause,
821+
memberNodes: node.members);
807822
}
808823

809824
/**
810825
* Record the given class as a subclass of its direct superclasses.
811826
*/
812827
void _addSubtypeForClassTypeAlis(ClassTypeAlias node) {
813-
_addSubtype(node.name.name, node.superclass, node.withClause,
814-
node.implementsClause, const []);
828+
_addSubtype(node.name.name,
829+
superclass: node.superclass,
830+
withClause: node.withClause,
831+
implementsClause: node.implementsClause,
832+
memberNodes: const []);
833+
}
834+
835+
/**
836+
* Record the given mixin as a subclass of its direct superclasses.
837+
*/
838+
void _addSubtypeForMixinDeclaration(MixinDeclaration node) {
839+
_addSubtype(node.name.name,
840+
onClause: node.onClause,
841+
implementsClause: node.implementsClause,
842+
memberNodes: node.members);
815843
}
816844

817845
/**
@@ -871,6 +899,9 @@ class _IndexContributor extends GeneralizingAstVisitor {
871899
for (InterfaceType mixinType in ancestor.mixins) {
872900
_recordIsAncestorOf(descendant, mixinType.element, true, visitedElements);
873901
}
902+
for (InterfaceType type in ancestor.superclassConstraints) {
903+
_recordIsAncestorOf(descendant, type.element, true, visitedElements);
904+
}
874905
for (InterfaceType implementedType in ancestor.interfaces) {
875906
_recordIsAncestorOf(
876907
descendant, implementedType.element, true, visitedElements);

pkg/analyzer/lib/src/dart/element/type.dart

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1511,6 +1511,15 @@ class InterfaceTypeImpl extends TypeImpl implements InterfaceType {
15111511
return true;
15121512
}
15131513
//
1514+
// I is listed in the on clause of J.
1515+
//
1516+
for (InterfaceType interfaceType in jElement.superclassConstraints) {
1517+
interfaceType = interfaceType.substitute2(jArgs, jVars);
1518+
if (interfaceType == i) {
1519+
return true;
1520+
}
1521+
}
1522+
//
15141523
// I is listed in the implements clause of J.
15151524
//
15161525
for (InterfaceType interfaceType in jElement.interfaces) {
@@ -2209,19 +2218,28 @@ class InterfaceTypeImpl extends TypeImpl implements InterfaceType {
22092218
int longestPath = 1;
22102219
try {
22112220
visitedTypes.add(classElement);
2212-
List<InterfaceType> superinterfaces = classElement.interfaces;
22132221
int pathLength;
2214-
if (superinterfaces.length > 0) {
2215-
// loop through each of the superinterfaces recursively calling this
2216-
// method and keeping track of the longest path to return
2217-
for (InterfaceType superinterface in superinterfaces) {
2218-
pathLength = _computeLongestInheritancePathToObject(
2219-
superinterface, depth + 1, visitedTypes);
2220-
if (pathLength > longestPath) {
2221-
longestPath = pathLength;
2222-
}
2222+
2223+
// loop through each of the superinterfaces recursively calling this
2224+
// method and keeping track of the longest path to return
2225+
for (InterfaceType interface in classElement.superclassConstraints) {
2226+
pathLength = _computeLongestInheritancePathToObject(
2227+
interface, depth + 1, visitedTypes);
2228+
if (pathLength > longestPath) {
2229+
longestPath = pathLength;
2230+
}
2231+
}
2232+
2233+
// loop through each of the superinterfaces recursively calling this
2234+
// method and keeping track of the longest path to return
2235+
for (InterfaceType interface in classElement.interfaces) {
2236+
pathLength = _computeLongestInheritancePathToObject(
2237+
interface, depth + 1, visitedTypes);
2238+
if (pathLength > longestPath) {
2239+
longestPath = pathLength;
22232240
}
22242241
}
2242+
22252243
// finally, perform this same check on the super type
22262244
// TODO(brianwilkerson) Does this also need to add in the number of mixin
22272245
// classes?

pkg/analyzer/test/src/dart/analysis/index_test.dart

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,27 @@ class C2 = Object with B;
101101
assertThat(classElementB)..isAncestorOf('C2 = Object with B');
102102
}
103103

104+
test_hasAncestor_MixinDeclaration() async {
105+
await _indexTestUnit('''
106+
class A {}
107+
class B extends A {}
108+
109+
mixin M1 on A {}
110+
mixin M2 on B {}
111+
mixin M3 implements A {}
112+
mixin M4 implements B {}
113+
mixin M5 on M2 {}
114+
''');
115+
ClassElement classElementA = findElement('A');
116+
assertThat(classElementA)
117+
..isAncestorOf('B extends A')
118+
..isAncestorOf('M1 on A')
119+
..isAncestorOf('M2 on B')
120+
..isAncestorOf('M3 implements A')
121+
..isAncestorOf('M4 implements B')
122+
..isAncestorOf('M5 on M2');
123+
}
124+
104125
test_isExtendedBy_ClassDeclaration() async {
105126
await _indexTestUnit('''
106127
class A {} // 1
@@ -1058,6 +1079,35 @@ class X extends dynamic {
10581079
expect(X.members, ['foo']);
10591080
}
10601081

1082+
test_subtypes_mixinDeclaration() async {
1083+
String libP = 'package:test/lib.dart;package:test/lib.dart';
1084+
provider.newFile(_p('$testProject/lib.dart'), '''
1085+
class A {}
1086+
class B {}
1087+
class C {}
1088+
class D {}
1089+
class E {}
1090+
''');
1091+
await _indexTestUnit('''
1092+
import 'lib.dart';
1093+
1094+
mixin X on A implements B, C {}
1095+
mixin Y on A, B implements C;
1096+
''');
1097+
1098+
{
1099+
var X = index.subtypes.singleWhere((t) => t.name == 'X');
1100+
expect(X.supertypes, ['$libP;A', '$libP;B', '$libP;C']);
1101+
expect(X.members, isEmpty);
1102+
}
1103+
1104+
{
1105+
var Y = index.subtypes.singleWhere((t) => t.name == 'Y');
1106+
expect(Y.supertypes, ['$libP;A', '$libP;B', '$libP;C']);
1107+
expect(Y.members, isEmpty);
1108+
}
1109+
}
1110+
10611111
test_usedName_inLibraryIdentifier() async {
10621112
await _indexTestUnit('''
10631113
library aaa.bbb.ccc;

0 commit comments

Comments
 (0)