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(