Resolve using mixin(s) in 'with' clauses.

This also allows searching for mixin(s), which we test now.

R=brianwilkerson@google.com

Change-Id: I6c1944db0e6ecbc0f7352188968e53ef38603426
Reviewed-on: https://dart-review.googlesource.com/72554
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
diff --git a/pkg/analysis_server/test/search/element_references_test.dart b/pkg/analysis_server/test/search/element_references_test.dart
index 2db2647..fb3aa88 100644
--- a/pkg/analysis_server/test/search/element_references_test.dart
+++ b/pkg/analysis_server/test/search/element_references_test.dart
@@ -408,6 +408,17 @@
     assertHasResult(SearchResultKind.REFERENCE, 'mmm);');
   }
 
+  Future<void> test_mixin() async {
+    addTestFile('''
+mixin A {}
+class B extends Object with A {} // B
+''');
+    await findElementReferences('A {}', false);
+    expect(searchElement.kind, ElementKind.MIXIN);
+    expect(results, hasLength(1));
+    assertHasResult(SearchResultKind.REFERENCE, 'A {} // B');
+  }
+
   Future<void> test_noElement() async {
     addTestFile('''
 main() {
diff --git a/pkg/analyzer/lib/src/dart/element/builder.dart b/pkg/analyzer/lib/src/dart/element/builder.dart
index 998a242..fa3788d 100644
--- a/pkg/analyzer/lib/src/dart/element/builder.dart
+++ b/pkg/analyzer/lib/src/dart/element/builder.dart
@@ -620,6 +620,7 @@
     element.typeParameters = holder.typeParameters;
     setElementDocumentationComment(element, node);
     element.accessors = holder.accessors;
+    element.constructors = holder.constructors;
     element.fields = holder.fields;
     element.methods = holder.methods;
     _currentHolder.addMixin(element);
diff --git a/pkg/analyzer/lib/src/dart/resolver/scope.dart b/pkg/analyzer/lib/src/dart/resolver/scope.dart
index f78400a..2e688d6 100644
--- a/pkg/analyzer/lib/src/dart/resolver/scope.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/scope.dart
@@ -638,6 +638,9 @@
         in compilationUnit.functionTypeAliases) {
       define(element);
     }
+    for (ClassElement element in compilationUnit.mixins) {
+      define(element);
+    }
     for (ClassElement element in compilationUnit.types) {
       define(element);
     }
diff --git a/pkg/analyzer/lib/src/summary/link.dart b/pkg/analyzer/lib/src/summary/link.dart
index f802e71..8bce0e0 100644
--- a/pkg/analyzer/lib/src/summary/link.dart
+++ b/pkg/analyzer/lib/src/summary/link.dart
@@ -552,6 +552,9 @@
    */
   final UnlinkedClass _unlinkedClass;
 
+  @override
+  final bool isMixin;
+
   List<ConstructorElementForLink> _constructors;
   ConstructorElementForLink _unnamedConstructor;
   bool _unnamedConstructorComputed = false;
@@ -563,8 +566,8 @@
   List<InterfaceType> _interfaces;
   List<PropertyAccessorElementForLink> _accessors;
 
-  ClassElementForLink_Class(
-      CompilationUnitElementForLink enclosingElement, this._unlinkedClass)
+  ClassElementForLink_Class(CompilationUnitElementForLink enclosingElement,
+      this._unlinkedClass, this.isMixin)
       : super(enclosingElement);
 
   @override
@@ -1001,6 +1004,7 @@
    */
   final String _absoluteUri;
 
+  List<ClassElementForLink_Class> _mixins;
   List<ClassElementForLink_Class> _types;
   Map<String, ReferenceableElementForLink> _containedNames;
   List<TopLevelVariableElementForLink> _topLevelVariables;
@@ -1130,6 +1134,17 @@
   LibraryElementForLink get library => enclosingElement;
 
   @override
+  List<ClassElementForLink_Class> get mixins {
+    if (_mixins == null) {
+      _mixins = <ClassElementForLink_Class>[];
+      for (UnlinkedClass unlinkedClass in _unlinkedUnit.mixins) {
+        _mixins.add(new ClassElementForLink_Class(this, unlinkedClass, true));
+      }
+    }
+    return _mixins;
+  }
+
+  @override
   ResynthesizerContext get resynthesizerContext => this;
 
   @override
@@ -1149,7 +1164,7 @@
     if (_types == null) {
       _types = <ClassElementForLink_Class>[];
       for (UnlinkedClass unlinkedClass in _unlinkedUnit.classes) {
-        _types.add(new ClassElementForLink_Class(this, unlinkedClass));
+        _types.add(new ClassElementForLink_Class(this, unlinkedClass, false));
       }
     }
     return _types;
diff --git a/pkg/analyzer/test/src/dart/analysis/index_test.dart b/pkg/analyzer/test/src/dart/analysis/index_test.dart
index d961a28..9a28fc6 100644
--- a/pkg/analyzer/test/src/dart/analysis/index_test.dart
+++ b/pkg/analyzer/test/src/dart/analysis/index_test.dart
@@ -360,7 +360,7 @@
       ..isInvokedAt('ggg(); // nq', false);
   }
 
-  test_isMixedInBy_ClassDeclaration() async {
+  test_isMixedInBy_ClassDeclaration_class() async {
     await _indexTestUnit('''
 class A {} // 1
 class B extends Object with A {} // 2
@@ -383,7 +383,18 @@
     assertThat(elementA).isMixedInAt('A {} // 2', true);
   }
 
-  test_isMixedInBy_ClassTypeAlias() async {
+  test_isMixedInBy_ClassDeclaration_mixin() async {
+    await _indexTestUnit('''
+mixin A {} // 1
+class B extends Object with A {} // 2
+''');
+    ClassElement elementA = findElement('A');
+    assertThat(elementA)
+      ..isMixedInAt('A {} // 2', false)
+      ..isReferencedAt('A {} // 2', false);
+  }
+
+  test_isMixedInBy_ClassTypeAlias_class() async {
     await _indexTestUnit('''
 class A {} // 1
 class B = Object with A; // 2
@@ -392,6 +403,15 @@
     assertThat(elementA).isMixedInAt('A; // 2', false);
   }
 
+  test_isMixedInBy_ClassTypeAlias_mixin() async {
+    await _indexTestUnit('''
+mixin A {} // 1
+class B = Object with A; // 2
+''');
+    ClassElement elementA = findElement('A');
+    assertThat(elementA).isMixedInAt('A; // 2', false);
+  }
+
   test_isReferencedBy_ClassElement() 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 2508050..cbadbb3 100644
--- a/pkg/analyzer/test/src/dart/analysis/search_test.dart
+++ b/pkg/analyzer/test/src/dart/analysis/search_test.dart
@@ -699,6 +699,19 @@
     await _verifyReferences(element, expected);
   }
 
+  test_searchReferences_ClassElement_mixin() async {
+    await _resolveTestUnit('''
+mixin A {}
+class B extends Object with A {} // with
+''');
+    ClassElement element = _findElementAtString('A {}');
+    Element b = _findElement('B');
+    var expected = [
+      _expectId(b, SearchResultKind.REFERENCE, 'A {} // with'),
+    ];
+    await _verifyReferences(element, expected);
+  }
+
   test_searchReferences_CompilationUnitElement() async {
     provider.newFile(_p('$testProject/foo.dart'), '');
     await _resolveTestUnit('''
diff --git a/pkg/analyzer/test/src/dart/resolution/mixin_test.dart b/pkg/analyzer/test/src/dart/resolution/mixin_test.dart
index 047499e..a39a2c3 100644
--- a/pkg/analyzer/test/src/dart/resolution/mixin_test.dart
+++ b/pkg/analyzer/test/src/dart/resolution/mixin_test.dart
@@ -89,6 +89,40 @@
     assertElementName(fields[0], 's', isSynthetic: true);
   }
 
+  test_classDeclaration_with() async {
+    addTestFile(r'''
+mixin M {}
+class A extends Object with M {} // A
+''');
+    await resolveTestFile();
+    assertNoTestErrors();
+
+    var mElement = findElement.mixin('M');
+
+    var aElement = findElement.class_('A');
+    assertElementTypes(aElement.mixins, [mElement.type]);
+
+    var mRef = findNode.typeName('M {} // A');
+    assertTypeName(mRef, mElement, 'M');
+  }
+
+  test_classTypeAlias_with() async {
+    addTestFile(r'''
+mixin M {}
+class A = Object with M;
+''');
+    await resolveTestFile();
+    assertNoTestErrors();
+
+    var mElement = findElement.mixin('M');
+
+    var aElement = findElement.class_('A');
+    assertElementTypes(aElement.mixins, [mElement.type]);
+
+    var mRef = findNode.typeName('M;');
+    assertTypeName(mRef, mElement, 'M');
+  }
+
   test_commentReference() async {
     addTestFile(r'''
 const a = 0;