Report MIXIN_DECLARES_CONSTRUCTOR and FINAL_NOT_INITIALIZED for mixins.

I renamed the previous error const to MIXIN_CLASS_DECLARES_CONSTRUCTOR.

R=brianwilkerson@google.com

Change-Id: If06f264e61b7d3e7c254eb457f4b2dc45ef76174
Reviewed-on: https://dart-review.googlesource.com/72860
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
diff --git a/pkg/analyzer/lib/error/error.dart b/pkg/analyzer/lib/error/error.dart
index 17387f6..d97f582 100644
--- a/pkg/analyzer/lib/error/error.dart
+++ b/pkg/analyzer/lib/error/error.dart
@@ -192,6 +192,7 @@
   CompileTimeErrorCode.MISSING_CONST_IN_LIST_LITERAL,
   CompileTimeErrorCode.MISSING_CONST_IN_MAP_LITERAL,
   CompileTimeErrorCode.MISSING_DART_LIBRARY,
+  CompileTimeErrorCode.MIXIN_CLASS_DECLARES_CONSTRUCTOR,
   CompileTimeErrorCode.MIXIN_DECLARES_CONSTRUCTOR,
   CompileTimeErrorCode.MIXIN_DEFERRED_CLASS,
   CompileTimeErrorCode.MIXIN_HAS_NO_CONSTRUCTORS,
diff --git a/pkg/analyzer/lib/src/dart/element/builder.dart b/pkg/analyzer/lib/src/dart/element/builder.dart
index fa3788d..567dd7c 100644
--- a/pkg/analyzer/lib/src/dart/element/builder.dart
+++ b/pkg/analyzer/lib/src/dart/element/builder.dart
@@ -82,25 +82,8 @@
       }
     }
 
-    ElementHolder holder = new ElementHolder();
-    //
-    // Process field declarations before constructors and methods so that field
-    // formal parameters can be correctly resolved to their fields.
-    //
-    ElementHolder previousHolder = _currentHolder;
-    _currentHolder = holder;
-    try {
-      List<ClassMember> nonFields = new List<ClassMember>();
-      node.visitChildren(
-          new _ElementBuilder_visitClassDeclaration(this, nonFields));
-      _buildFieldMap(holder.fieldsWithoutFlushing);
-      int count = nonFields.length;
-      for (int i = 0; i < count; i++) {
-        nonFields[i].accept(this);
-      }
-    } finally {
-      _currentHolder = previousHolder;
-    }
+    ElementHolder holder = _buildClassMembers(node);
+
     SimpleIdentifier className = node.name;
     ClassElementImpl element = new ClassElementImpl.forNode(className);
     _setCodeRange(element, node);
@@ -604,14 +587,7 @@
 
   @override
   Object visitMixinDeclaration(MixinDeclaration node) {
-    ElementHolder holder = new ElementHolder();
-    ElementHolder previousHolder = _currentHolder;
-    _currentHolder = holder;
-    try {
-      node.visitChildren(this);
-    } finally {
-      _currentHolder = previousHolder;
-    }
+    ElementHolder holder = _buildClassMembers(node);
 
     SimpleIdentifier nameNode = node.name;
     MixinElementImpl element = new MixinElementImpl.forNode(nameNode);
@@ -721,6 +697,29 @@
     return null;
   }
 
+  ElementHolder _buildClassMembers(AstNode classNode) {
+    ElementHolder holder = new ElementHolder();
+    //
+    // Process field declarations before constructors and methods so that field
+    // formal parameters can be correctly resolved to their fields.
+    //
+    ElementHolder previousHolder = _currentHolder;
+    _currentHolder = holder;
+    try {
+      List<ClassMember> nonFields = new List<ClassMember>();
+      classNode.visitChildren(
+          new _ClassNotExecutableElementsBuilder(this, nonFields));
+      _buildFieldMap(holder.fieldsWithoutFlushing);
+      int count = nonFields.length;
+      for (int i = 0; i < count; i++) {
+        nonFields[i].accept(this);
+      }
+    } finally {
+      _currentHolder = previousHolder;
+    }
+    return holder;
+  }
+
   /**
    * Build the table mapping field names to field elements for the [fields]
    * defined in the current class.
@@ -1695,12 +1694,14 @@
   }
 }
 
-class _ElementBuilder_visitClassDeclaration extends UnifyingAstVisitor<Object> {
+/**
+ * Builds elements for all node that are not constructors or methods.
+ */
+class _ClassNotExecutableElementsBuilder extends UnifyingAstVisitor<Object> {
   final ApiElementBuilder builder;
+  final List<ClassMember> nonFields;
 
-  List<ClassMember> nonFields;
-
-  _ElementBuilder_visitClassDeclaration(this.builder, this.nonFields) : super();
+  _ClassNotExecutableElementsBuilder(this.builder, this.nonFields);
 
   @override
   Object visitConstructorDeclaration(ConstructorDeclaration node) {
diff --git a/pkg/analyzer/lib/src/error/codes.dart b/pkg/analyzer/lib/src/error/codes.dart
index 7663cc3..3bbd44a 100644
--- a/pkg/analyzer/lib/src/error/codes.dart
+++ b/pkg/analyzer/lib/src/error/codes.dart
@@ -1670,13 +1670,21 @@
    * Parameters:
    * 0: the name of the mixin that is invalid
    */
-  static const CompileTimeErrorCode MIXIN_DECLARES_CONSTRUCTOR =
+  static const CompileTimeErrorCode MIXIN_CLASS_DECLARES_CONSTRUCTOR =
       const CompileTimeErrorCode(
-          'MIXIN_DECLARES_CONSTRUCTOR',
+          'MIXIN_CLASS_DECLARES_CONSTRUCTOR',
           "The class '{0}' can't be used as a mixin because it declares a "
           "constructor.");
 
   /**
+   * The <i>mixinMember</i> production allows the same instance or static
+   * members that a class would allow, but no constructors (for now).
+   */
+  static const CompileTimeErrorCode MIXIN_DECLARES_CONSTRUCTOR =
+      const CompileTimeErrorCode(
+          'MIXIN_DECLARES_CONSTRUCTOR', "Mixins can't declare constructors.");
+
+  /**
    * 9.1 Mixin Application: It is a compile-time error if the with clause of a
    * mixin application <i>C</i> includes a deferred type expression.
    *
diff --git a/pkg/analyzer/lib/src/generated/error_verifier.dart b/pkg/analyzer/lib/src/generated/error_verifier.dart
index f18c8d2..70d9148 100644
--- a/pkg/analyzer/lib/src/generated/error_verifier.dart
+++ b/pkg/analyzer/lib/src/generated/error_verifier.dart
@@ -501,8 +501,9 @@
           withClause != null) {
         _checkClassInheritance(node, superclass, withClause, implementsClause);
       }
-      visitClassDeclarationIncrementally(node);
-      _checkForFinalNotInitializedInClass(node);
+
+      _initializeInitialFieldElementsMap(_enclosingClass.fields);
+      _checkForFinalNotInitializedInClass(node.members);
       _checkForDuplicateDefinitionInheritance();
       _checkForConflictingInstanceMethodSetter(node);
       _checkForBadFunctionUse(node);
@@ -514,28 +515,6 @@
     }
   }
 
-  /**
-   * Implementation of this method should be synchronized with
-   * [visitClassDeclaration].
-   */
-  void visitClassDeclarationIncrementally(ClassDeclaration node) {
-    _isInNativeClass = node.nativeClause != null;
-    _enclosingClass = AbstractClassElementImpl.getImpl(node.declaredElement);
-    // initialize initialFieldElementsMap
-    if (_enclosingClass != null) {
-      List<FieldElement> fieldElements = _enclosingClass.fields;
-      _initialFieldElementsMap = new HashMap<FieldElement, INIT_STATE>();
-      for (FieldElement fieldElement in fieldElements) {
-        if (!fieldElement.isSynthetic) {
-          _initialFieldElementsMap[fieldElement] =
-              fieldElement.initializer == null
-                  ? INIT_STATE.NOT_INIT
-                  : INIT_STATE.INIT_IN_DECLARATION;
-        }
-      }
-    }
-  }
-
   @override
   Object visitClassTypeAlias(ClassTypeAlias node) {
     _checkForBuiltInIdentifierAsName(
@@ -592,6 +571,7 @@
       _checkForConflictingConstructorNameAndMember(node, constructorElement);
       _checkForAllFinalInitializedErrorCodes(node);
       _checkForRedirectingConstructorErrorCodes(node);
+      _checkForMixinDeclaresConstructor(node);
       _checkForMultipleSuperInitializers(node);
       _checkForRecursiveConstructorRedirect(node, constructorElement);
       if (!_checkForRecursiveFactoryRedirect(node, constructorElement)) {
@@ -1053,7 +1033,7 @@
 //    ClassElementImpl outerClass = _enclosingClass;
     try {
 //      _isInNativeClass = node.nativeClause != null;
-//      _enclosingClass = AbstractClassElementImpl.getImpl(node.declaredElement);
+      _enclosingClass = AbstractClassElementImpl.getImpl(node.declaredElement);
 //      _checkDuplicateClassMembers(node);
 //      _checkForBuiltInIdentifierAsName(
 //          node.name, CompileTimeErrorCode.BUILT_IN_IDENTIFIER_AS_TYPE_NAME);
@@ -1068,15 +1048,16 @@
       if (onClause != null || implementsClause != null) {
         _checkMixinInheritance(node, onClause, implementsClause);
       }
-//      visitClassDeclarationIncrementally(node);
-//      _checkForFinalNotInitializedInClass(node);
+
+      _initializeInitialFieldElementsMap(_enclosingClass.fields);
+      _checkForFinalNotInitializedInClass(node.members);
 //      _checkForDuplicateDefinitionInheritance();
 //      _checkForConflictingInstanceMethodSetter(node);
 //      _checkForBadFunctionUse(node);
       return super.visitMixinDeclaration(node);
     } finally {
 //      _isInNativeClass = false;
-//      _initialFieldElementsMap = null;
+      _initialFieldElementsMap = null;
 //      _enclosingClass = outerClass;
     }
   }
@@ -1816,7 +1797,7 @@
   /**
    * Verify that all classes of the given [withClause] are valid.
    *
-   * See [CompileTimeErrorCode.MIXIN_DECLARES_CONSTRUCTOR],
+   * See [CompileTimeErrorCode.MIXIN_CLASS_DECLARES_CONSTRUCTOR],
    * [CompileTimeErrorCode.MIXIN_INHERITS_FROM_NOT_OBJECT], and
    * [CompileTimeErrorCode.MIXIN_REFERENCES_SUPER].
    */
@@ -1837,7 +1818,7 @@
               mixinName, CompileTimeErrorCode.MIXIN_DEFERRED_CLASS)) {
             problemReported = true;
           }
-          if (_checkForMixinDeclaresConstructor(mixinName, mixinElement)) {
+          if (_checkForMixinClassDeclaresConstructor(mixinName, mixinElement)) {
             problemReported = true;
           }
           if (!enableSuperMixins &&
@@ -3510,23 +3491,20 @@
   }
 
   /**
-   * Verify that final fields in the given class [declaration] that are
-   * declared, without any constructors in the enclosing class, are
-   * initialized. Cases in which there is at least one constructor are handled
-   * at the end of
-   * [_checkForAllFinalInitializedErrorCodes].
+   * If there are no constructors in the given [members], verify that all
+   * final fields are initialized.  Cases in which there is at least one
+   * constructor are handled in [_checkForAllFinalInitializedErrorCodes].
    *
    * See [CompileTimeErrorCode.CONST_NOT_INITIALIZED], and
    * [StaticWarningCode.FINAL_NOT_INITIALIZED].
    */
-  void _checkForFinalNotInitializedInClass(ClassDeclaration declaration) {
-    NodeList<ClassMember> classMembers = declaration.members;
-    for (ClassMember classMember in classMembers) {
+  void _checkForFinalNotInitializedInClass(List<ClassMember> members) {
+    for (ClassMember classMember in members) {
       if (classMember is ConstructorDeclaration) {
         return;
       }
     }
-    for (ClassMember classMember in classMembers) {
+    for (ClassMember classMember in members) {
       if (classMember is FieldDeclaration) {
         _checkForFinalNotInitialized(classMember.fields);
       }
@@ -4459,14 +4437,14 @@
    * constructor. The [mixinName] is the node to report problem on. The
    * [mixinElement] is the mixing to evaluate.
    *
-   * See [CompileTimeErrorCode.MIXIN_DECLARES_CONSTRUCTOR].
+   * See [CompileTimeErrorCode.MIXIN_CLASS_DECLARES_CONSTRUCTOR].
    */
-  bool _checkForMixinDeclaresConstructor(
+  bool _checkForMixinClassDeclaresConstructor(
       TypeName mixinName, ClassElement mixinElement) {
     for (ConstructorElement constructor in mixinElement.constructors) {
       if (!constructor.isSynthetic && !constructor.isFactory) {
         _errorReporter.reportErrorForNode(
-            CompileTimeErrorCode.MIXIN_DECLARES_CONSTRUCTOR,
+            CompileTimeErrorCode.MIXIN_CLASS_DECLARES_CONSTRUCTOR,
             mixinName,
             [mixinElement.name]);
         return true;
@@ -4475,6 +4453,13 @@
     return false;
   }
 
+  void _checkForMixinDeclaresConstructor(ConstructorDeclaration node) {
+    if (_enclosingClass.isMixin) {
+      _errorReporter.reportErrorForNode(
+          CompileTimeErrorCode.MIXIN_DECLARES_CONSTRUCTOR, node.returnType);
+    }
+  }
+
   /**
    * Report the error [CompileTimeErrorCode.MIXIN_HAS_NO_CONSTRUCTORS] if
    * appropriate.
@@ -4954,7 +4939,7 @@
   /**
    * Verify that all classes of the given [onClause] are valid.
    *
-   * See [CompileTimeErrorCode.MIXIN_DECLARES_CONSTRUCTOR],
+   * See [CompileTimeErrorCode.MIXIN_CLASS_DECLARES_CONSTRUCTOR],
    * [CompileTimeErrorCode.MIXIN_INHERITS_FROM_NOT_OBJECT], and
    * [CompileTimeErrorCode.MIXIN_REFERENCES_SUPER].
    */
@@ -6323,6 +6308,18 @@
     return visitor.hasSelfReference;
   }
 
+  void _initializeInitialFieldElementsMap(List<FieldElement> fields) {
+    _initialFieldElementsMap = new HashMap<FieldElement, INIT_STATE>();
+    for (FieldElement fieldElement in fields) {
+      if (!fieldElement.isSynthetic) {
+        _initialFieldElementsMap[fieldElement] =
+            fieldElement.initializer == null
+                ? INIT_STATE.NOT_INIT
+                : INIT_STATE.INIT_IN_DECLARATION;
+      }
+    }
+  }
+
   bool _isFunctionType(DartType type) {
     if (type.isDynamic || type.isDartCoreNull) {
       return true;
diff --git a/pkg/analyzer/test/generated/compile_time_error_code_test.dart b/pkg/analyzer/test/generated/compile_time_error_code_test.dart
index 4d6b0b7..0463112 100644
--- a/pkg/analyzer/test/generated/compile_time_error_code_test.dart
+++ b/pkg/analyzer/test/generated/compile_time_error_code_test.dart
@@ -4055,37 +4055,31 @@
     verify([source]);
   }
 
-  @failingTest
-  test_mixinDeclaresConstructor() async {
-    Source source = addSource(r'''
-class A {
-  A() {}
-}
-class B extends Object mixin A {}''');
-    await computeAnalysisResult(source);
-    assertErrors(source, [CompileTimeErrorCode.MIXIN_DECLARES_CONSTRUCTOR]);
-    verify([source]);
-  }
-
-  test_mixinDeclaresConstructor_classDeclaration() async {
+  test_mixinClassDeclaresConstructor_classDeclaration() async {
     Source source = addSource(r'''
 class A {
   A() {}
 }
 class B extends Object with A {}''');
     await computeAnalysisResult(source);
-    assertErrors(source, [CompileTimeErrorCode.MIXIN_DECLARES_CONSTRUCTOR]);
+    assertErrors(
+      source,
+      [CompileTimeErrorCode.MIXIN_CLASS_DECLARES_CONSTRUCTOR],
+    );
     verify([source]);
   }
 
-  test_mixinDeclaresConstructor_typeAlias() async {
+  test_mixinClassDeclaresConstructor_typeAlias() async {
     Source source = addSource(r'''
 class A {
   A() {}
 }
 class B = Object with A;''');
     await computeAnalysisResult(source);
-    assertErrors(source, [CompileTimeErrorCode.MIXIN_DECLARES_CONSTRUCTOR]);
+    assertErrors(
+      source,
+      [CompileTimeErrorCode.MIXIN_CLASS_DECLARES_CONSTRUCTOR],
+    );
     verify([source]);
   }
 
diff --git a/pkg/analyzer/test/generated/simple_resolver_test.dart b/pkg/analyzer/test/generated/simple_resolver_test.dart
index 732ed20..f38ced1 100644
--- a/pkg/analyzer/test/generated/simple_resolver_test.dart
+++ b/pkg/analyzer/test/generated/simple_resolver_test.dart
@@ -1070,7 +1070,10 @@
     ClassElement a = unit.getType('A');
     expect(a.isValidMixin, isFalse);
     await computeAnalysisResult(source);
-    assertErrors(source, [CompileTimeErrorCode.MIXIN_DECLARES_CONSTRUCTOR]);
+    assertErrors(
+      source,
+      [CompileTimeErrorCode.MIXIN_CLASS_DECLARES_CONSTRUCTOR],
+    );
     verify([source]);
   }
 
@@ -1088,7 +1091,10 @@
     ClassElement a = unit.getType('A');
     expect(a.isValidMixin, isFalse);
     await computeAnalysisResult(source);
-    assertErrors(source, [CompileTimeErrorCode.MIXIN_DECLARES_CONSTRUCTOR]);
+    assertErrors(
+      source,
+      [CompileTimeErrorCode.MIXIN_CLASS_DECLARES_CONSTRUCTOR],
+    );
     verify([source]);
   }
 
diff --git a/pkg/analyzer/test/src/dart/resolution/find_element.dart b/pkg/analyzer/test/src/dart/resolution/find_element.dart
index df7c82b..0b1ed90 100644
--- a/pkg/analyzer/test/src/dart/resolution/find_element.dart
+++ b/pkg/analyzer/test/src/dart/resolution/find_element.dart
@@ -48,6 +48,13 @@
   }
 
   FieldElement field(String name) {
+    for (var type in unitElement.mixins) {
+      for (var field in type.fields) {
+        if (field.name == name) {
+          return field;
+        }
+      }
+    }
     for (var type in unitElement.types) {
       for (var field in type.fields) {
         if (field.name == name) {
diff --git a/pkg/analyzer/test/src/dart/resolution/find_node.dart b/pkg/analyzer/test/src/dart/resolution/find_node.dart
index 54b907a..55998cc 100644
--- a/pkg/analyzer/test/src/dart/resolution/find_node.dart
+++ b/pkg/analyzer/test/src/dart/resolution/find_node.dart
@@ -28,10 +28,18 @@
     return _node(search, (n) => n is CommentReference);
   }
 
+  ConstructorDeclaration constructor(String search) {
+    return _node(search, (n) => n is ConstructorDeclaration);
+  }
+
   ExportDirective export(String search) {
     return _node(search, (n) => n is ExportDirective);
   }
 
+  FieldFormalParameter fieldFormalParameter(String search) {
+    return _node(search, (n) => n is FieldFormalParameter);
+  }
+
   FunctionExpression functionExpression(String search) {
     return _node(search, (n) => n is FunctionExpression);
   }
diff --git a/pkg/analyzer/test/src/dart/resolution/mixin_test.dart b/pkg/analyzer/test/src/dart/resolution/mixin_test.dart
index a39a2c3..d1bb294 100644
--- a/pkg/analyzer/test/src/dart/resolution/mixin_test.dart
+++ b/pkg/analyzer/test/src/dart/resolution/mixin_test.dart
@@ -1,3 +1,4 @@
+import 'package:analyzer/dart/element/element.dart';
 import 'package:analyzer/src/error/codes.dart';
 import 'package:test/test.dart';
 import 'package:test_reflective_loader/test_reflective_loader.dart';
@@ -153,6 +154,60 @@
     assertElementTypes(element.superclassConstraints, [objectType]);
   }
 
+  test_error_finalNotInitialized() async {
+    addTestFile(r'''
+mixin M {
+  final int f;
+}
+''');
+    await resolveTestFile();
+    assertTestErrors([StaticWarningCode.FINAL_NOT_INITIALIZED]);
+  }
+
+  test_error_finalNotInitialized_OK() async {
+    addTestFile(r'''
+mixin M {
+  final int f = 0;
+}
+''');
+    await resolveTestFile();
+    assertNoTestErrors();
+  }
+
+  test_error_finalNotInitializedConstructor() async {
+    addTestFile(r'''
+mixin M {
+  final int f;
+  M();
+}
+''');
+    await resolveTestFile();
+    assertTestErrors([
+      CompileTimeErrorCode.MIXIN_DECLARES_CONSTRUCTOR,
+      StaticWarningCode.FINAL_NOT_INITIALIZED_CONSTRUCTOR_1,
+    ]);
+  }
+
+  test_error_finalNotInitializedConstructor_OK() async {
+    addTestFile(r'''
+mixin M {
+  final int f;
+  M(this.f);
+}
+''');
+    await resolveTestFile();
+    assertTestErrors([CompileTimeErrorCode.MIXIN_DECLARES_CONSTRUCTOR]);
+
+    var element = findElement.mixin('M');
+    var constructorElement = element.constructors.single;
+
+    var fpNode = findNode.fieldFormalParameter('f);');
+    assertElement(fpNode.identifier, constructorElement.parameters[0]);
+
+    FieldFormalParameterElement fpElement = fpNode.declaredElement;
+    expect(fpElement.field, same(findElement.field('f')));
+  }
+
   test_error_implementsClause_deferredClass() async {
     addTestFile(r'''
 import 'dart:math' deferred as math;
@@ -208,6 +263,37 @@
     assertTypeName(typeRef, null, 'void');
   }
 
+  test_error_mixinDeclaresConstructor() async {
+    addTestFile(r'''
+mixin M {
+  M(int a) {
+    a; // read
+  }
+}
+''');
+    await resolveTestFile();
+    assertTestErrors([CompileTimeErrorCode.MIXIN_DECLARES_CONSTRUCTOR]);
+
+    // Even though it is an error for a mixin to declare a constructor,
+    // we still build elements for constructors, and resolve them.
+
+    var element = findElement.mixin('M');
+    var constructors = element.constructors;
+    expect(constructors, hasLength(1));
+    var constructorElement = constructors[0];
+
+    var constructorNode = findNode.constructor('M(int a)');
+    assertElement(constructorNode, constructorElement);
+
+    var aElement = constructorElement.parameters[0];
+    var aNode = constructorNode.parameters.parameters[0];
+    assertElement(aNode, aElement);
+
+    var aRef = findNode.simple('a; // read');
+    assertElement(aRef, aElement);
+    assertType(aRef, 'int');
+  }
+
   test_error_onClause_deferredClass() async {
     addTestFile(r'''
 import 'dart:math' deferred as math;
diff --git a/pkg/analyzer/test/src/dart/resolution/resolution.dart b/pkg/analyzer/test/src/dart/resolution/resolution.dart
index a7751c1..eacf271 100644
--- a/pkg/analyzer/test/src/dart/resolution/resolution.dart
+++ b/pkg/analyzer/test/src/dart/resolution/resolution.dart
@@ -199,6 +199,8 @@
       return node.staticElement;
     } else if (node is Declaration) {
       return node.declaredElement;
+    } else if (node is FormalParameter) {
+      return node.declaredElement;
     } else if (node is Identifier) {
       return node.staticElement;
     } else if (node is IndexExpression) {