Index and search for mixins.
R=brianwilkerson@google.com
Change-Id: I5399507dc5a9688ee5944f6554167a6b1e8692b6
Reviewed-on: https://dart-review.googlesource.com/72525
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
diff --git a/pkg/analysis_server/lib/plugin/protocol/protocol_dart.dart b/pkg/analysis_server/lib/plugin/protocol/protocol_dart.dart
index 8ef13df..4d46fa6 100644
--- a/pkg/analysis_server/lib/plugin/protocol/protocol_dart.dart
+++ b/pkg/analysis_server/lib/plugin/protocol/protocol_dart.dart
@@ -100,8 +100,13 @@
* Return an [ElementKind] corresponding to the given [engine.Element].
*/
ElementKind convertElementToElementKind(engine.Element element) {
- if (element is engine.ClassElement && element.isEnum) {
- return ElementKind.ENUM;
+ if (element is engine.ClassElement) {
+ if (element.isEnum) {
+ return ElementKind.ENUM;
+ }
+ if (element.isMixin) {
+ return ElementKind.MIXIN;
+ }
}
if (element is engine.FieldElement &&
element.isEnumConstant &&
diff --git a/pkg/analysis_server/test/plugin/protocol_dart_test.dart b/pkg/analysis_server/test/plugin/protocol_dart_test.dart
index 0a46a17..5799565 100644
--- a/pkg/analysis_server/test/plugin/protocol_dart_test.dart
+++ b/pkg/analysis_server/test/plugin/protocol_dart_test.dart
@@ -184,7 +184,7 @@
expect(element.parameters, '(int a, {@required int c, int b})');
}
- // Verify parameter re-ordering for required params
+ /// Verify parameter re-ordering for required params
test_fromElement_CONSTRUCTOR_required_parameters_2() async {
addMetaPackageSource();
engine.Source source = addSource('/test.dart', '''
@@ -202,7 +202,7 @@
'(int a, {@required int d, @required int c, int b})');
}
- // Verify parameter re-ordering for required params
+ /// Verify parameter re-ordering for required params
test_fromElement_CONSTRUCTOR_required_parameters_3() async {
addMetaPackageSource();
engine.Source source = addSource('/test.dart', '''
@@ -486,6 +486,31 @@
expect(element.flags, Element.FLAG_STATIC);
}
+ test_fromElement_MIXIN() async {
+ engine.Source source = addSource('/test.dart', '''
+mixin A {}
+''');
+ engine.CompilationUnit unit = await resolveLibraryUnit(source);
+ {
+ engine.ClassElement engineElement = findElementInUnit(unit, 'A');
+ // create notification Element
+ Element element = convertElement(engineElement);
+ expect(element.kind, ElementKind.MIXIN);
+ expect(element.name, 'A');
+ expect(element.typeParameters, isNull);
+ {
+ Location location = element.location;
+ expect(location.file, convertPath('/test.dart'));
+ expect(location.offset, 6);
+ expect(location.length, 'A'.length);
+ expect(location.startLine, 1);
+ expect(location.startColumn, 7);
+ }
+ expect(element.parameters, isNull);
+ expect(element.flags, 0);
+ }
+ }
+
test_fromElement_SETTER() async {
engine.Source source = addSource('/test.dart', '''
class A {
diff --git a/pkg/analysis_server/test/services/search/search_engine_test.dart b/pkg/analysis_server/test/services/search/search_engine_test.dart
index 9d0b654..c8a7acf 100644
--- a/pkg/analysis_server/test/services/search/search_engine_test.dart
+++ b/pkg/analysis_server/test/services/search/search_engine_test.dart
@@ -193,9 +193,9 @@
var searchEngine = new SearchEngineImpl([driver]);
Set<ClassElement> subtypes = await searchEngine.searchAllSubtypes(element);
expect(subtypes, hasLength(3));
- expect(subtypes, contains(predicate((ClassElement e) => e.name == 'A')));
- expect(subtypes, contains(predicate((ClassElement e) => e.name == 'B')));
- expect(subtypes, contains(predicate((ClassElement e) => e.name == 'C')));
+ _assertContainsClass(subtypes, 'A');
+ _assertContainsClass(subtypes, 'B');
+ _assertContainsClass(subtypes, 'C');
}
test_searchAllSubtypes_acrossDrivers() async {
@@ -226,6 +226,35 @@
expect(subtypes, contains(predicate((ClassElement e) => e.name == 'C')));
}
+ test_searchAllSubtypes_mixin() async {
+ var p = newFile('/test.dart', content: '''
+class T {}
+
+mixin A on T {}
+mixin B implements T {}
+
+class C extends T {}
+
+mixin D on C {}
+mixin E implements C {}
+''').path;
+
+ var driver = _newDriver();
+ driver.addFile(p);
+
+ var resultA = await driver.getResult(p);
+ ClassElement element = resultA.unit.declaredElement.types[0];
+
+ var searchEngine = new SearchEngineImpl([driver]);
+ Set<ClassElement> subtypes = await searchEngine.searchAllSubtypes(element);
+ expect(subtypes, hasLength(5));
+ _assertContainsClass(subtypes, 'A');
+ _assertContainsClass(subtypes, 'B');
+ _assertContainsClass(subtypes, 'C');
+ _assertContainsClass(subtypes, 'D');
+ _assertContainsClass(subtypes, 'E');
+ }
+
test_searchMemberDeclarations() async {
var codeA = '''
class A {
@@ -389,10 +418,9 @@
}
var searchEngine = new SearchEngineImpl([driver1, driver2]);
- List<SearchMatch> matches =
- await searchEngine.searchTopLevelDeclarations('.*');
- expect(
- matches.where((match) => !match.libraryElement.isInSdk), hasLength(4));
+ var matches = await searchEngine.searchTopLevelDeclarations('.*');
+ matches.removeWhere((match) => match.libraryElement.isInSdk);
+ expect(matches, hasLength(4));
void assertHasOneElement(String name) {
Iterable<SearchMatch> nameMatches = matches.where((SearchMatch m) =>
@@ -470,4 +498,8 @@
new SourceFactory(resolvers, null, resourceProvider),
new AnalysisOptionsImpl());
}
+
+ static void _assertContainsClass(Set<ClassElement> subtypes, String name) {
+ expect(subtypes, contains(predicate((ClassElement e) => e.name == name)));
+ }
}
diff --git a/pkg/analyzer/lib/src/dart/analysis/defined_names.dart b/pkg/analyzer/lib/src/dart/analysis/defined_names.dart
index 58d0a65..44baa00 100644
--- a/pkg/analyzer/lib/src/dart/analysis/defined_names.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/defined_names.dart
@@ -33,6 +33,9 @@
if (member is ClassDeclaration) {
member.members.forEach(appendClassMemberName);
}
+ if (member is MixinDeclaration) {
+ member.members.forEach(appendClassMemberName);
+ }
} else if (member is TopLevelVariableDeclaration) {
for (VariableDeclaration variable in member.variables.variables) {
appendName(names.topLevelNames, variable.name);
diff --git a/pkg/analyzer/lib/src/dart/analysis/driver.dart b/pkg/analyzer/lib/src/dart/analysis/driver.dart
index 12f3249..37bd368 100644
--- a/pkg/analyzer/lib/src/dart/analysis/driver.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/driver.dart
@@ -92,7 +92,7 @@
/**
* The version of data format, should be incremented on every format change.
*/
- static const int DATA_VERSION = 64;
+ static const int DATA_VERSION = 65;
/**
* The number of exception contexts allowed to write. Once this field is
diff --git a/pkg/analyzer/lib/src/dart/analysis/index.dart b/pkg/analyzer/lib/src/dart/analysis/index.dart
index 13153c0..1afbb1d 100644
--- a/pkg/analyzer/lib/src/dart/analysis/index.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/index.dart
@@ -627,6 +627,13 @@
}
@override
+ visitOnClause(OnClause node) {
+ for (TypeName typeName in node.superclassConstraints) {
+ recordSuperType(typeName, IndexRelationKind.IS_IMPLEMENTED_BY);
+ }
+ }
+
+ @override
visitPartDirective(PartDirective node) {
CompilationUnitElement element = node.element;
if (element?.source != null) {
diff --git a/pkg/analyzer/lib/src/dart/analysis/search.dart b/pkg/analyzer/lib/src/dart/analysis/search.dart
index 38b6a6a..b998712 100644
--- a/pkg/analyzer/lib/src/dart/analysis/search.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/search.dart
@@ -85,7 +85,7 @@
Search(this._driver);
/**
- * Returns class members with the given [name].
+ * Returns class or mixin members with the given [name].
*/
Future<List<Element>> classMembers(String name) async {
// TODO(brianwilkerson) Determine whether this await is necessary.
@@ -98,15 +98,18 @@
}
}
+ void addElements(ClassElement element) {
+ element.accessors.forEach(addElement);
+ element.fields.forEach(addElement);
+ element.methods.forEach(addElement);
+ }
+
List<String> files = await _driver.getFilesDefiningClassMemberName(name);
for (String file in files) {
UnitElementResult unitResult = await _driver.getUnitElement(file);
if (unitResult != null) {
- for (ClassElement clazz in unitResult.element.types) {
- clazz.accessors.forEach(addElement);
- clazz.fields.forEach(addElement);
- clazz.methods.forEach(addElement);
- }
+ unitResult.element.types.forEach(addElements);
+ unitResult.element.mixins.forEach(addElements);
}
}
return elements;
@@ -439,6 +442,7 @@
unitElement.enums.forEach(addElement);
unitElement.functions.forEach(addElement);
unitElement.functionTypeAliases.forEach(addElement);
+ unitElement.mixins.forEach(addElement);
unitElement.topLevelVariables.forEach(addElement);
unitElement.types.forEach(addElement);
}
diff --git a/pkg/analyzer/lib/src/dart/element/element.dart b/pkg/analyzer/lib/src/dart/element/element.dart
index 15245a4..e3c4b49 100644
--- a/pkg/analyzer/lib/src/dart/element/element.dart
+++ b/pkg/analyzer/lib/src/dart/element/element.dart
@@ -1805,6 +1805,7 @@
safelyVisitChildren(enums, visitor);
safelyVisitChildren(functions, visitor);
safelyVisitChildren(functionTypeAliases, visitor);
+ safelyVisitChildren(mixins, visitor);
safelyVisitChildren(types, visitor);
safelyVisitChildren(topLevelVariables, visitor);
}
diff --git a/pkg/analyzer/lib/src/summary/resynthesize.dart b/pkg/analyzer/lib/src/summary/resynthesize.dart
index 7c20b56..355b249 100644
--- a/pkg/analyzer/lib/src/summary/resynthesize.dart
+++ b/pkg/analyzer/lib/src/summary/resynthesize.dart
@@ -272,6 +272,7 @@
unitElement.enums.forEach(putElement);
unitElement.functions.forEach(putElement);
unitElement.functionTypeAliases.forEach(putElement);
+ unitElement.mixins.forEach(putElement);
unitElement.topLevelVariables.forEach(putElement);
unitElement.types.forEach(putElement);
unitsInLibrary[unitUri] = elementsInUnit;
diff --git a/pkg/analyzer/test/src/dart/analysis/defined_names_test.dart b/pkg/analyzer/test/src/dart/analysis/defined_names_test.dart
index 6b5131c..336414a 100644
--- a/pkg/analyzer/test/src/dart/analysis/defined_names_test.dart
+++ b/pkg/analyzer/test/src/dart/analysis/defined_names_test.dart
@@ -17,7 +17,7 @@
@reflectiveTest
class DefinedNamesTest extends ParserTestCase {
- test_classMemberNames() {
+ test_classMemberNames_class() {
DefinedNames names = _computeDefinedNames('''
class A {
int a, b;
@@ -36,6 +36,25 @@
unorderedEquals(['a', 'b', 'd', 'e', 'f', 'g']));
}
+ test_classMemberNames_mixin() {
+ DefinedNames names = _computeDefinedNames('''
+mixin A {
+ int a, b;
+ A();
+ A.c();
+ d() {}
+ get e => null;
+ set f(_) {}
+}
+mixin B {
+ g() {}
+}
+''');
+ expect(names.topLevelNames, unorderedEquals(['A', 'B']));
+ expect(names.classMemberNames,
+ unorderedEquals(['a', 'b', 'd', 'e', 'f', 'g']));
+ }
+
test_topLevelNames() {
DefinedNames names = _computeDefinedNames('''
class A {}
@@ -45,9 +64,10 @@
get E => null;
set F(_) {}
var G, H;
+mixin M {}
''');
expect(names.topLevelNames,
- unorderedEquals(['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H']));
+ unorderedEquals(['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'M']));
expect(names.classMemberNames, isEmpty);
}
diff --git a/pkg/analyzer/test/src/dart/analysis/driver_test.dart b/pkg/analyzer/test/src/dart/analysis/driver_test.dart
index b533f9b..d8db996 100644
--- a/pkg/analyzer/test/src/dart/analysis/driver_test.dart
+++ b/pkg/analyzer/test/src/dart/analysis/driver_test.dart
@@ -1365,7 +1365,7 @@
} on ArgumentError {}
}
- test_getFilesDefiningClassMemberName() async {
+ test_getFilesDefiningClassMemberName_class() async {
var a = _p('/test/bin/a.dart');
var b = _p('/test/bin/b.dart');
var c = _p('/test/bin/c.dart');
@@ -1391,6 +1391,32 @@
unorderedEquals([d]));
}
+ test_getFilesDefiningClassMemberName_mixin() async {
+ var a = _p('/test/bin/a.dart');
+ var b = _p('/test/bin/b.dart');
+ var c = _p('/test/bin/c.dart');
+ var d = _p('/test/bin/d.dart');
+
+ provider.newFile(a, 'mixin A { m1() {} }');
+ provider.newFile(b, 'mixin B { m2() {} }');
+ provider.newFile(c, 'mixin C { m2() {} }');
+ provider.newFile(d, 'mixin D { m3() {} }');
+
+ driver.addFile(a);
+ driver.addFile(b);
+ driver.addFile(c);
+ driver.addFile(d);
+
+ expect(await driver.getFilesDefiningClassMemberName('m1'),
+ unorderedEquals([a]));
+
+ expect(await driver.getFilesDefiningClassMemberName('m2'),
+ unorderedEquals([b, c]));
+
+ expect(await driver.getFilesDefiningClassMemberName('m3'),
+ unorderedEquals([d]));
+ }
+
test_getFilesReferencingName() async {
var a = _p('/test/bin/a.dart');
var b = _p('/test/bin/b.dart');
diff --git a/pkg/analyzer/test/src/dart/analysis/index_test.dart b/pkg/analyzer/test/src/dart/analysis/index_test.dart
index dbd664a..d961a28 100644
--- a/pkg/analyzer/test/src/dart/analysis/index_test.dart
+++ b/pkg/analyzer/test/src/dart/analysis/index_test.dart
@@ -197,6 +197,28 @@
..isReferencedAt('B; // 3', false);
}
+ test_isImplementedBy_MixinDeclaration_implementsClause() async {
+ await _indexTestUnit('''
+class A {} // 1
+mixin M implements A {} // 2
+''');
+ ClassElement elementA = findElement('A');
+ assertThat(elementA)
+ ..isImplementedAt('A {} // 2', false)
+ ..isReferencedAt('A {} // 2', false);
+ }
+
+ test_isImplementedBy_MixinDeclaration_onClause() async {
+ await _indexTestUnit('''
+class A {} // 1
+mixin M on A {} // 2
+''');
+ ClassElement elementA = findElement('A');
+ assertThat(elementA)
+ ..isImplementedAt('A {} // 2', false)
+ ..isReferencedAt('A {} // 2', false);
+ }
+
test_isInvokedBy_FieldElement() async {
await _indexTestUnit('''
class A {
diff --git a/pkg/analyzer/test/src/dart/analysis/search_test.dart b/pkg/analyzer/test/src/dart/analysis/search_test.dart
index 9567ef9a..2508050 100644
--- a/pkg/analyzer/test/src/dart/analysis/search_test.dart
+++ b/pkg/analyzer/test/src/dart/analysis/search_test.dart
@@ -73,7 +73,7 @@
CompilationUnitElement testUnitElement;
LibraryElement testLibraryElement;
- test_classMembers() async {
+ test_classMembers_class() async {
await _resolveTestUnit('''
class A {
test() {}
@@ -99,6 +99,25 @@
expect(await driver.search.classMembers('test'), isEmpty);
}
+ test_classMembers_mixin() async {
+ await _resolveTestUnit('''
+mixin A {
+ test() {}
+}
+mixin B {
+ int test = 1;
+ int testTwo = 2;
+ main() {
+ int test = 3;
+ }
+}
+''');
+ ClassElement a = _findElement('A');
+ ClassElement b = _findElement('B');
+ expect(await driver.search.classMembers('test'),
+ unorderedEquals([a.methods[0], b.fields[0]]));
+ }
+
test_declarations_class() async {
await _resolveTestUnit('''
class C {
@@ -1666,6 +1685,22 @@
await _verifyReferences(element, expected);
}
+ test_searchSubtypes_mixinDeclaration() async {
+ await _resolveTestUnit('''
+class T {}
+mixin A on T {} // A
+mixin B implements T {} // B
+''');
+ ClassElement element = _findElement('T');
+ ClassElement a = _findElement('A');
+ ClassElement b = _findElement('B');
+ var expected = [
+ _expectId(a, SearchResultKind.REFERENCE, 'T {} // A'),
+ _expectId(b, SearchResultKind.REFERENCE, 'T {} // B'),
+ ];
+ await _verifyReferences(element, expected);
+ }
+
test_subtypes() async {
await _resolveTestUnit('''
class A {}
@@ -1867,19 +1902,21 @@
await _resolveTestUnit('''
class A {} // A
class B = Object with A;
-typedef C();
-D() {}
-var e = null;
-class NoMatchABCDE {}
+mixin C {}
+typedef D();
+e() {}
+var f = null;
+class NoMatchABCDEF {}
''');
Element a = _findElement('A');
Element b = _findElement('B');
Element c = _findElement('C');
Element d = _findElement('D');
Element e = _findElement('e');
- RegExp regExp = new RegExp(r'^[ABCDe]$');
+ Element f = _findElement('f');
+ RegExp regExp = new RegExp(r'^[ABCDef]$');
expect(await driver.search.topLevelElements(regExp),
- unorderedEquals([a, b, c, d, e]));
+ unorderedEquals([a, b, c, d, e, f]));
}
Declaration _assertHasDeclaration(