Resolve mixin declarations.

Errors for onClause are not correct yet, and not tested yet.

R=brianwilkerson@google.com

Change-Id: I9a1f2b4734661baac85a9d1b9311d21104c7c495
Reviewed-on: https://dart-review.googlesource.com/72360
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
diff --git a/pkg/analyzer/lib/dart/ast/ast.dart b/pkg/analyzer/lib/dart/ast/ast.dart
index 84c538a..777bdc3 100644
--- a/pkg/analyzer/lib/dart/ast/ast.dart
+++ b/pkg/analyzer/lib/dart/ast/ast.dart
@@ -5185,6 +5185,9 @@
  * Clients may not extend, implement or mix-in this class.
  */
 abstract class MixinDeclaration extends NamedCompilationUnitMember {
+  @override
+  ClassElement get declaredElement;
+
   /**
    * Return the implements clause for the mixin, or `null` if the mixin does not
    * implement any interfaces.
diff --git a/pkg/analyzer/lib/src/dart/ast/ast.dart b/pkg/analyzer/lib/src/dart/ast/ast.dart
index c655ac4..573adfe 100644
--- a/pkg/analyzer/lib/src/dart/ast/ast.dart
+++ b/pkg/analyzer/lib/src/dart/ast/ast.dart
@@ -7816,8 +7816,7 @@
     ..add(rightBracket);
 
   @override
-//  MixinElement get declaredElement => _name?.staticElement as MixinElement;
-  Element get declaredElement => throw new UnimplementedError();
+  ClassElement get declaredElement => _name?.staticElement as ClassElement;
 
   @deprecated
   @override
diff --git a/pkg/analyzer/lib/src/generated/declaration_resolver.dart b/pkg/analyzer/lib/src/generated/declaration_resolver.dart
index e2ee324..4a24786 100644
--- a/pkg/analyzer/lib/src/generated/declaration_resolver.dart
+++ b/pkg/analyzer/lib/src/generated/declaration_resolver.dart
@@ -398,6 +398,16 @@
   }
 
   @override
+  Object visitMixinDeclaration(MixinDeclaration node) {
+    ClassElement element = _match(node.name, _walker.getMixin());
+    _walk(new ElementWalker.forClass(element), () {
+      super.visitMixinDeclaration(node);
+    });
+    resolveMetadata(node, node.metadata, element);
+    return null;
+  }
+
+  @override
   Object visitPartDirective(PartDirective node) {
     super.visitPartDirective(node);
     List<ElementAnnotation> annotations =
@@ -839,6 +849,8 @@
   int _enumIndex = 0;
   List<ExecutableElement> _functions;
   int _functionIndex = 0;
+  List<ClassElement> _mixins;
+  int _mixinIndex = 0;
   List<ParameterElement> _parameters;
   int _parameterIndex = 0;
   List<FunctionTypeAliasElement> _typedefs;
@@ -872,6 +884,7 @@
         _classes = compilationUnit.types,
         _enums = compilationUnit.enums,
         _functions = compilationUnit.functions,
+        _mixins = compilationUnit.mixins,
         _typedefs = compilationUnit.functionTypeAliases,
         _variables =
             compilationUnit.topLevelVariables.where(_isNotSynthetic).toList();
@@ -977,6 +990,12 @@
   ExecutableElement getFunction() => _functions[_functionIndex++];
 
   /**
+   * Returns the next non-synthetic child of [element] which is a mixin; throws
+   * an [IndexError] if there are no more.
+   */
+  ClassElement getMixin() => _mixins[_mixinIndex++];
+
+  /**
    * Returns the next non-synthetic child of [element] which is a parameter;
    * throws an [IndexError] if there are no more.
    */
diff --git a/pkg/analyzer/lib/src/generated/element_resolver.dart b/pkg/analyzer/lib/src/generated/element_resolver.dart
index d8b1349..974721e 100644
--- a/pkg/analyzer/lib/src/generated/element_resolver.dart
+++ b/pkg/analyzer/lib/src/generated/element_resolver.dart
@@ -716,6 +716,12 @@
   }
 
   @override
+  Object visitMixinDeclaration(MixinDeclaration node) {
+    resolveMetadata(node);
+    return null;
+  }
+
+  @override
   Object visitPartDirective(PartDirective node) {
     resolveMetadata(node);
     return null;
diff --git a/pkg/analyzer/lib/src/generated/parser_fasta.dart b/pkg/analyzer/lib/src/generated/parser_fasta.dart
index 43b0b5f..8cbdd21 100644
--- a/pkg/analyzer/lib/src/generated/parser_fasta.dart
+++ b/pkg/analyzer/lib/src/generated/parser_fasta.dart
@@ -27,6 +27,7 @@
       {bool allowNativeClause: false})
       : fastaParser = new fasta.Parser(null),
         astBuilder = new AstBuilder(errorReporter, fileUri, true) {
+    fastaParser.isMixinSupportEnabled = true;
     fastaParser.listener = astBuilder;
     astBuilder.parser = fastaParser;
     astBuilder.allowNativeClause = allowNativeClause;
diff --git a/pkg/analyzer/lib/src/generated/resolver.dart b/pkg/analyzer/lib/src/generated/resolver.dart
index bbe3d04..8329fb9 100644
--- a/pkg/analyzer/lib/src/generated/resolver.dart
+++ b/pkg/analyzer/lib/src/generated/resolver.dart
@@ -5025,6 +5025,12 @@
    */
   ExecutableElement _enclosingFunction = null;
 
+  /**
+   * The mixin declaration representing the class containing the current node,
+   * or `null` if the current node is not contained in a mixin.
+   */
+  MixinDeclaration _enclosingMixinDeclaration = null;
+
   InferenceContext inferenceContext = null;
 
   /**
@@ -5309,7 +5315,8 @@
   Object visitAnnotation(Annotation node) {
     AstNode parent = node.parent;
     if (identical(parent, _enclosingClassDeclaration) ||
-        identical(parent, _enclosingFunctionTypeAlias)) {
+        identical(parent, _enclosingFunctionTypeAlias) ||
+        identical(parent, _enclosingMixinDeclaration)) {
       return null;
     }
     node.name?.accept(this);
@@ -6211,6 +6218,31 @@
   }
 
   @override
+  Object visitMixinDeclaration(MixinDeclaration node) {
+    //
+    // Resolve the metadata in the library scope.
+    //
+    node.metadata?.accept(this);
+    _enclosingMixinDeclaration = node;
+    //
+    // Continue the class resolution.
+    //
+    ClassElement outerType = enclosingClass;
+    try {
+      enclosingClass = node.declaredElement;
+      typeAnalyzer.thisType = enclosingClass?.type;
+      super.visitMixinDeclaration(node);
+      node.accept(elementResolver);
+      node.accept(typeAnalyzer);
+    } finally {
+      typeAnalyzer.thisType = outerType?.type;
+      enclosingClass = outerType;
+      _enclosingMixinDeclaration = null;
+    }
+    return null;
+  }
+
+  @override
   Object visitNamedExpression(NamedExpression node) {
     InferenceContext.setTypeFromNode(node.expression, node);
     return super.visitNamedExpression(node);
@@ -7645,6 +7677,40 @@
     super.visitMethodDeclaration(node);
   }
 
+  @override
+  Object visitMixinDeclaration(MixinDeclaration node) {
+    ClassElement element = node.declaredElement;
+
+    Scope outerScope = nameScope;
+    ClassElement outerClass = enclosingClass;
+    try {
+      enclosingClass = element;
+
+      nameScope = new TypeParameterScope(nameScope, element);
+      visitMixinDeclarationInScope(node);
+
+      nameScope = new ClassScope(nameScope, element);
+      visitMixinMembersInScope(node);
+    } finally {
+      nameScope = outerScope;
+      enclosingClass = outerClass;
+    }
+    return null;
+  }
+
+  void visitMixinDeclarationInScope(MixinDeclaration node) {
+    node.name?.accept(this);
+    node.typeParameters?.accept(this);
+    node.onClause?.accept(this);
+    node.implementsClause?.accept(this);
+  }
+
+  void visitMixinMembersInScope(MixinDeclaration node) {
+    node.documentationComment?.accept(this);
+    node.metadata.accept(this);
+    node.members.accept(this);
+  }
+
   /**
    * Visit the given statement after it's scope has been created. This is used by ResolverVisitor to
    * correctly visit the 'then' and 'else' statements of an 'if' statement.
@@ -9694,7 +9760,8 @@
       }
       classElement.supertype = superclassType;
     }
-    _resolve(classElement, withClause, implementsClause);
+    _resolveWithClause(classElement, withClause);
+    _resolveImplementsClause(classElement, implementsClause);
     return null;
   }
 
@@ -9736,7 +9803,8 @@
     if (classElement != null) {
       classElement.supertype = superclassType;
     }
-    _resolve(classElement, node.withClause, node.implementsClause);
+    _resolveWithClause(classElement, node.withClause);
+    _resolveImplementsClause(classElement, node.implementsClause);
     return null;
   }
 
@@ -9915,6 +9983,15 @@
   }
 
   @override
+  void visitMixinDeclarationInScope(MixinDeclaration node) {
+    super.visitMixinDeclarationInScope(node);
+    MixinElementImpl element = node.declaredElement;
+    _resolveOnClause(element, node.onClause);
+    _resolveImplementsClause(element, node.implementsClause);
+    return null;
+  }
+
+  @override
   Object visitNode(AstNode node) {
     // In API mode we need to skip:
     //   - function bodies;
@@ -10180,24 +10257,10 @@
     return null;
   }
 
-  /**
-   * Resolve the types in the given [withClause] and [implementsClause] and
-   * associate those types with the given [classElement].
-   */
-  void _resolve(ClassElementImpl classElement, WithClause withClause,
-      ImplementsClause implementsClause) {
-    if (withClause != null) {
-      List<InterfaceType> mixinTypes = _resolveTypes(
-          withClause.mixinTypes,
-          CompileTimeErrorCode.MIXIN_OF_NON_CLASS,
-          CompileTimeErrorCode.MIXIN_OF_ENUM,
-          CompileTimeErrorCode.MIXIN_OF_NON_CLASS);
-      if (classElement != null) {
-        classElement.mixins = mixinTypes;
-      }
-    }
-    if (implementsClause != null) {
-      NodeList<TypeName> interfaces = implementsClause.interfaces;
+  void _resolveImplementsClause(
+      ClassElementImpl classElement, ImplementsClause clause) {
+    if (clause != null) {
+      NodeList<TypeName> interfaces = clause.interfaces;
       List<InterfaceType> interfaceTypes = _resolveTypes(
           interfaces,
           CompileTimeErrorCode.IMPLEMENTS_NON_CLASS,
@@ -10232,6 +10295,20 @@
     }
   }
 
+  void _resolveOnClause(MixinElementImpl classElement, OnClause clause) {
+    List<InterfaceType> types;
+    if (clause != null) {
+      types = _resolveTypes(
+          clause.superclassConstraints,
+          CompileTimeErrorCode.MIXIN_OF_NON_CLASS,
+          CompileTimeErrorCode.MIXIN_OF_ENUM,
+          CompileTimeErrorCode.MIXIN_OF_NON_CLASS);
+    } else {
+      types = [typeProvider.objectType];
+    }
+    classElement.superclassConstraints = types;
+  }
+
   /**
    * Return the type specified by the given name.
    *
@@ -10294,6 +10371,17 @@
     return types;
   }
 
+  void _resolveWithClause(ClassElementImpl classElement, WithClause clause) {
+    if (clause != null) {
+      List<InterfaceType> mixinTypes = _resolveTypes(
+          clause.mixinTypes,
+          CompileTimeErrorCode.MIXIN_OF_NON_CLASS,
+          CompileTimeErrorCode.MIXIN_OF_ENUM,
+          CompileTimeErrorCode.MIXIN_OF_NON_CLASS);
+      classElement.mixins = mixinTypes;
+    }
+  }
+
   /**
    * Given a function typed [parameter] with [FunctionType] based on a
    * [GenericFunctionTypeElementImpl], compute and set the return type for the
diff --git a/pkg/analyzer/lib/src/task/dart.dart b/pkg/analyzer/lib/src/task/dart.dart
index 5f96198..ee2ccc0 100644
--- a/pkg/analyzer/lib/src/task/dart.dart
+++ b/pkg/analyzer/lib/src/task/dart.dart
@@ -3761,8 +3761,6 @@
     parser.parseFunctionBodies =
         options.analyzeFunctionBodiesPredicate(_source);
     parser.enableOptionalNewAndConst = true;
-    (parser as ParserAdapter).fastaParser.isMixinSupportEnabled =
-        (options as AnalysisOptionsImpl).isMixinSupportEnabled;
     CompilationUnit unit = parser.parseCompilationUnit(tokenStream);
     unit.lineInfo = lineInfo;
 
diff --git a/pkg/analyzer/test/generated/hint_code_test.dart b/pkg/analyzer/test/generated/hint_code_test.dart
index 6619450..d0ab205 100644
--- a/pkg/analyzer/test/generated/hint_code_test.dart
+++ b/pkg/analyzer/test/generated/hint_code_test.dart
@@ -1660,7 +1660,6 @@
     verify([source]);
   }
 
-  @failingTest
   test_invalidSealedAnnotation_onMixin() async {
     Source source = addNamedSource('/lib1.dart', r'''
 import 'package:meta/meta.dart';
diff --git a/pkg/analyzer/test/src/dart/resolution/find_element.dart b/pkg/analyzer/test/src/dart/resolution/find_element.dart
index ce924b6..ff87f0e 100644
--- a/pkg/analyzer/test/src/dart/resolution/find_element.dart
+++ b/pkg/analyzer/test/src/dart/resolution/find_element.dart
@@ -1,5 +1,6 @@
 import 'package:analyzer/dart/ast/ast.dart';
 import 'package:analyzer/dart/element/element.dart';
+import 'package:analyzer/dart/element/type.dart';
 import 'package:test/test.dart';
 
 import 'function_ast_visitor.dart';
@@ -85,6 +86,10 @@
     fail('Not found import: $targetUri');
   }
 
+  InterfaceType interfaceType(String name) {
+    return class_(name).type;
+  }
+
   LocalVariableElement localVar(String name) {
     LocalVariableElement result;
     unit.accept(new FunctionAstVisitor(
@@ -115,6 +120,15 @@
     fail('Not found class method: $name');
   }
 
+  ClassElement mixin(String name) {
+    for (var mixin in unitElement.mixins) {
+      if (mixin.name == name) {
+        return mixin;
+      }
+    }
+    fail('Not found mixin: $name');
+  }
+
   ParameterElement parameter(String name) {
     ParameterElement parameterElement;
     void considerParameter(ParameterElement parameter) {
diff --git a/pkg/analyzer/test/src/dart/resolution/find_node.dart b/pkg/analyzer/test/src/dart/resolution/find_node.dart
index ac80c6c..54b907a 100644
--- a/pkg/analyzer/test/src/dart/resolution/find_node.dart
+++ b/pkg/analyzer/test/src/dart/resolution/find_node.dart
@@ -12,116 +12,138 @@
     return unit.directives.singleWhere((d) => d is LibraryDirective);
   }
 
+  Annotation annotation(String search) {
+    return _node(search, (n) => n is Annotation);
+  }
+
   AssignmentExpression assignment(String search) {
-    return _node(search).getAncestor((n) => n is AssignmentExpression);
+    return _node(search, (n) => n is AssignmentExpression);
   }
 
   CascadeExpression cascade(String search) {
-    return _node(search).getAncestor((n) => n is CascadeExpression);
+    return _node(search, (n) => n is CascadeExpression);
+  }
+
+  CommentReference commentReference(String search) {
+    return _node(search, (n) => n is CommentReference);
   }
 
   ExportDirective export(String search) {
-    return _node(search).getAncestor((n) => n is ExportDirective);
+    return _node(search, (n) => n is ExportDirective);
   }
 
   FunctionExpression functionExpression(String search) {
-    return _node(search).getAncestor((n) => n is FunctionExpression);
+    return _node(search, (n) => n is FunctionExpression);
   }
 
   GenericFunctionType genericFunctionType(String search) {
-    return _node(search).getAncestor((n) => n is GenericFunctionType);
+    return _node(search, (n) => n is GenericFunctionType);
   }
 
   ImportDirective import(String search) {
-    return _node(search).getAncestor((n) => n is ImportDirective);
+    return _node(search, (n) => n is ImportDirective);
   }
 
   InstanceCreationExpression instanceCreation(String search) {
-    return _node(search).getAncestor((n) => n is InstanceCreationExpression);
+    return _node(search, (n) => n is InstanceCreationExpression);
   }
 
   ListLiteral listLiteral(String search) {
-    return _node(search).getAncestor((n) => n is ListLiteral);
+    return _node(search, (n) => n is ListLiteral);
   }
 
   MapLiteral mapLiteral(String search) {
-    return _node(search).getAncestor((n) => n is MapLiteral);
+    return _node(search, (n) => n is MapLiteral);
+  }
+
+  MethodDeclaration methodDeclaration(String search) {
+    return _node(search, (n) => n is MethodDeclaration);
   }
 
   MethodInvocation methodInvocation(String search) {
-    return _node(search).getAncestor((n) => n is MethodInvocation);
+    return _node(search, (n) => n is MethodInvocation);
+  }
+
+  MixinDeclaration mixin(String search) {
+    return _node(search, (n) => n is MixinDeclaration);
   }
 
   ParenthesizedExpression parenthesized(String search) {
-    return _node(search).getAncestor((n) => n is ParenthesizedExpression);
+    return _node(search, (n) => n is ParenthesizedExpression);
   }
 
   PartDirective part(String search) {
-    return _node(search).getAncestor((n) => n is PartDirective);
+    return _node(search, (n) => n is PartDirective);
   }
 
   PartOfDirective partOf(String search) {
-    return _node(search).getAncestor((n) => n is PartOfDirective);
+    return _node(search, (n) => n is PartOfDirective);
   }
 
   PostfixExpression postfix(String search) {
-    return _node(search).getAncestor((n) => n is PostfixExpression);
+    return _node(search, (n) => n is PostfixExpression);
   }
 
   PrefixExpression prefix(String search) {
-    return _node(search).getAncestor((n) => n is PrefixExpression);
+    return _node(search, (n) => n is PrefixExpression);
   }
 
   PrefixedIdentifier prefixed(String search) {
-    return _node(search).getAncestor((n) => n is PrefixedIdentifier);
+    return _node(search, (n) => n is PrefixedIdentifier);
   }
 
   RethrowExpression rethrow_(String search) {
-    return _node(search).getAncestor((n) => n is RethrowExpression);
+    return _node(search, (n) => n is RethrowExpression);
   }
 
   SimpleIdentifier simple(String search) {
-    return _node(search);
+    return _node(search, (_) => true);
   }
 
   SimpleFormalParameter simpleParameter(String search) {
-    return _node(search).getAncestor((n) => n is SimpleFormalParameter);
+    return _node(search, (n) => n is SimpleFormalParameter);
   }
 
   StringLiteral stringLiteral(String search) {
-    return _node(search).getAncestor((n) => n is StringLiteral);
+    return _node(search, (n) => n is StringLiteral);
   }
 
   SuperExpression super_(String search) {
-    return _node(search).getAncestor((n) => n is SuperExpression);
+    return _node(search, (n) => n is SuperExpression);
   }
 
   ThisExpression this_(String search) {
-    return _node(search).getAncestor((n) => n is ThisExpression);
+    return _node(search, (n) => n is ThisExpression);
   }
 
   ThrowExpression throw_(String search) {
-    return _node(search).getAncestor((n) => n is ThrowExpression);
+    return _node(search, (n) => n is ThrowExpression);
   }
 
   TypeName typeName(String search) {
-    return _node(search).getAncestor((n) => n is TypeName);
+    return _node(search, (n) => n is TypeName);
   }
 
   TypeParameter typeParameter(String search) {
-    return _node(search).getAncestor((n) => n is TypeParameter);
+    return _node(search, (n) => n is TypeParameter);
   }
 
   VariableDeclaration variableDeclaration(String search) {
-    return _node(search).getAncestor((n) => n is VariableDeclaration);
+    return _node(search, (n) => n is VariableDeclaration);
   }
 
-  AstNode _node(String search) {
+  AstNode _node(String search, bool Function(AstNode) predicate) {
     var index = content.indexOf(search);
     if (content.indexOf(search, index + 1) != -1) {
       fail('The pattern |$search| is not unique in:\n$content');
     }
     expect(index, greaterThanOrEqualTo(0));
-    return new NodeLocator2(index).searchWithin(unit);
+
+    var node = new NodeLocator2(index).searchWithin(unit);
+    expect(node, isNotNull);
+
+    var result = node.getAncestor(predicate);
+    expect(result, isNotNull);
+    return result;
   }
 }
diff --git a/pkg/analyzer/test/src/dart/resolution/mixin_test.dart b/pkg/analyzer/test/src/dart/resolution/mixin_test.dart
new file mode 100644
index 0000000..205c71f
--- /dev/null
+++ b/pkg/analyzer/test/src/dart/resolution/mixin_test.dart
@@ -0,0 +1,222 @@
+import 'package:test/test.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import 'driver_resolution.dart';
+import 'resolution.dart';
+import 'task_resolution.dart';
+
+main() {
+  defineReflectiveSuite(() {
+    defineReflectiveTests(MixinDriverResolutionTest);
+    defineReflectiveTests(MixinTaskResolutionTest);
+  });
+}
+
+abstract class AssignmentResolutionMixin implements ResolutionTest {
+  test_accessor_getter() async {
+    addTestFile(r'''
+mixin M {
+  int get g => 0;
+}
+''');
+    await resolveTestFile();
+
+    var element = findElement.mixin('M');
+
+    var accessors = element.accessors;
+    expect(accessors, hasLength(1));
+
+    var gElement = accessors[0];
+    assertElementName(gElement, 'g', offset: 20);
+
+    var gNode = findNode.methodDeclaration('g =>');
+    assertElement(gNode.name, gElement);
+
+    var fields = element.fields;
+    expect(fields, hasLength(1));
+    assertElementName(fields[0], 'g', isSynthetic: true);
+  }
+
+  test_accessor_method() async {
+    addTestFile(r'''
+mixin M {
+  void foo() {}
+}
+''');
+    await resolveTestFile();
+
+    var element = findElement.mixin('M');
+
+    var methods = element.methods;
+    expect(methods, hasLength(1));
+
+    var fooElement = methods[0];
+    assertElementName(fooElement, 'foo', offset: 17);
+
+    var fooNode = findNode.methodDeclaration('foo()');
+    assertElement(fooNode.name, fooElement);
+  }
+
+  test_accessor_setter() async {
+    addTestFile(r'''
+mixin M {
+  void set s(int _) {}
+}
+''');
+    await resolveTestFile();
+
+    var element = findElement.mixin('M');
+
+    var accessors = element.accessors;
+    expect(accessors, hasLength(1));
+
+    var sElement = accessors[0];
+    assertElementName(sElement, 's=', offset: 21);
+
+    var gNode = findNode.methodDeclaration('s(int _)');
+    assertElement(gNode.name, sElement);
+
+    var fields = element.fields;
+    expect(fields, hasLength(1));
+    assertElementName(fields[0], 's', isSynthetic: true);
+  }
+
+  test_commentReference() async {
+    addTestFile(r'''
+const a = 0;
+
+/// Reference [a] in documentation.
+mixin M {}
+''');
+    await resolveTestFile();
+
+    var aRef = findNode.commentReference('a]').identifier;
+    assertElement(aRef, findElement.topGet('a'));
+    assertTypeNull(aRef);
+  }
+
+  test_element() async {
+    addTestFile(r'''
+mixin M {}
+''');
+    await resolveTestFile();
+
+    var mixin = findNode.mixin('mixin M');
+    var element = findElement.mixin('M');
+    assertElement(mixin, element);
+
+    expect(element.typeParameters, isEmpty);
+    assertElementTypes(element.superclassConstraints, [objectType]);
+  }
+
+  test_field() async {
+    addTestFile(r'''
+mixin M<T> {
+  T f;
+}
+''');
+    await resolveTestFile();
+
+    var element = findElement.mixin('M');
+
+    var typeParameters = element.typeParameters;
+    expect(typeParameters, hasLength(1));
+
+    var tElement = typeParameters.single;
+    assertElementName(tElement, 'T', offset: 8);
+    assertEnclosingElement(tElement, element);
+
+    var tNode = findNode.typeParameter('T> {');
+    assertElement(tNode.name, tElement);
+
+    var fields = element.fields;
+    expect(fields, hasLength(1));
+
+    var fElement = fields[0];
+    assertElementName(fElement, 'f', offset: 17);
+    assertEnclosingElement(fElement, element);
+
+    var fNode = findNode.variableDeclaration('f;');
+    assertElement(fNode.name, fElement);
+
+    assertTypeName(findNode.typeName('T f'), tElement, 'T');
+
+    var accessors = element.accessors;
+    expect(accessors, hasLength(2));
+    assertElementName(accessors[0], 'f', isSynthetic: true);
+    assertElementName(accessors[1], 'f=', isSynthetic: true);
+  }
+
+  test_implementsClause() async {
+    addTestFile(r'''
+class A {}
+class B {}
+
+mixin M implements A, B {} // M
+''');
+    await resolveTestFile();
+
+    var element = findElement.mixin('M');
+    assertElementTypes(element.interfaces, [
+      findElement.interfaceType('A'),
+      findElement.interfaceType('B'),
+    ]);
+
+    var aRef = findNode.typeName('A, ');
+    assertTypeName(aRef, findElement.class_('A'), 'A');
+
+    var bRef = findNode.typeName('B {} // M');
+    assertTypeName(bRef, findElement.class_('B'), 'B');
+  }
+
+  test_metadata() async {
+    addTestFile(r'''
+const a = 0;
+
+@a
+mixin M {}
+''');
+    await resolveTestFile();
+
+    var a = findElement.topGet('a');
+    var element = findElement.mixin('M');
+
+    var metadata = element.metadata;
+    expect(metadata, hasLength(1));
+    expect(metadata[0].element, same(a));
+
+    var annotation = findNode.annotation('@a');
+    assertElement(annotation, a);
+    expect(annotation.elementAnnotation, same(metadata[0]));
+  }
+
+  test_onClause() async {
+    addTestFile(r'''
+class A {}
+class B {}
+
+mixin M on A, B {} // M
+''');
+    await resolveTestFile();
+
+    var element = findElement.mixin('M');
+    assertElementTypes(element.superclassConstraints, [
+      findElement.interfaceType('A'),
+      findElement.interfaceType('B'),
+    ]);
+
+    var aRef = findNode.typeName('A, ');
+    assertTypeName(aRef, findElement.class_('A'), 'A');
+
+    var bRef = findNode.typeName('B {} // M');
+    assertTypeName(bRef, findElement.class_('B'), 'B');
+  }
+}
+
+@reflectiveTest
+class MixinDriverResolutionTest extends DriverResolutionTest
+    with AssignmentResolutionMixin {}
+
+@reflectiveTest
+class MixinTaskResolutionTest extends TaskResolutionTest
+    with AssignmentResolutionMixin {}
diff --git a/pkg/analyzer/test/src/dart/resolution/resolution.dart b/pkg/analyzer/test/src/dart/resolution/resolution.dart
index 9533052..b93718a 100644
--- a/pkg/analyzer/test/src/dart/resolution/resolution.dart
+++ b/pkg/analyzer/test/src/dart/resolution/resolution.dart
@@ -41,6 +41,8 @@
 
   ClassElement get numElement => typeProvider.numType.element;
 
+  InterfaceType get objectType => typeProvider.objectType;
+
   TypeProvider get typeProvider =>
       result.unit.declaredElement.context.typeProvider;
 
@@ -53,11 +55,35 @@
     expect(actual, same(expected));
   }
 
+  void assertElementName(Element element, String name,
+      {bool isSynthetic = false, int offset}) {
+    expect(element.name, name);
+    expect(element.isSynthetic, isSynthetic);
+    if (offset != null) {
+      expect(element.nameOffset, offset);
+    }
+  }
+
   void assertElementNull(Expression node) {
     Element actual = getNodeElement(node);
     expect(actual, isNull);
   }
 
+  void assertElementType(DartType type, DartType expected) {
+    expect(type, expected);
+  }
+
+  void assertElementTypes(List<DartType> types, List<DartType> expected) {
+    expect(types, hasLength(expected.length));
+    for (var i = 0; i < types.length; ++i) {
+      assertElementType(types[i], expected[i]);
+    }
+  }
+
+  void assertEnclosingElement(Element element, Element expectedEnclosing) {
+    expect(element.enclosingElement, expectedEnclosing);
+  }
+
   void assertIdentifierTopGetRef(SimpleIdentifier ref, String name) {
     var getter = findElement.topGet(name);
     assertElement(ref, getter);
@@ -129,9 +155,13 @@
     expect(node.staticType, isNull);
   }
 
-  Element getNodeElement(Expression node) {
-    if (node is AssignmentExpression) {
+  Element getNodeElement(AstNode node) {
+    if (node is Annotation) {
+      return node.element;
+    } else if (node is AssignmentExpression) {
       return node.staticElement;
+    } else if (node is Declaration) {
+      return node.declaredElement;
     } else if (node is Identifier) {
       return node.staticElement;
     } else if (node is IndexExpression) {