Version 2.10.0-105.0.dev

Merge commit 'f8fbefd951af2a21f5f85bb6d711b9d0bf636af2' into 'dev'
diff --git a/DEPS b/DEPS
index 0b5e7df..8d2dda6 100644
--- a/DEPS
+++ b/DEPS
@@ -97,7 +97,7 @@
   "dart_style_tag": "1.3.7",  # Please see the note above before updating.
 
   "chromedriver_tag": "83.0.4103.39",
-  "dartdoc_rev" : "291ebc50072746bc59ccab59115a298915218428",
+  "dartdoc_rev" : "b039e21a7226b61ca2de7bd6c7a07fc77d4f64a9",
   "ffi_rev": "454ab0f9ea6bd06942a983238d8a6818b1357edb",
   "fixnum_rev": "16d3890c6dc82ca629659da1934e412292508bba",
   "glob_rev": "e9f4e6b7ae8abe5071461cf8f47191bb19cf7ef6",
diff --git a/pkg/analysis_server/lib/src/services/correction/bulk_fix_processor.dart b/pkg/analysis_server/lib/src/services/correction/bulk_fix_processor.dart
index 09dd636..78eb8de 100644
--- a/pkg/analysis_server/lib/src/services/correction/bulk_fix_processor.dart
+++ b/pkg/analysis_server/lib/src/services/correction/bulk_fix_processor.dart
@@ -8,6 +8,7 @@
 import 'package:analysis_server/src/services/correction/change_workspace.dart';
 import 'package:analysis_server/src/services/correction/dart/abstract_producer.dart';
 import 'package:analysis_server/src/services/correction/dart/add_await.dart';
+import 'package:analysis_server/src/services/correction/dart/add_const.dart';
 import 'package:analysis_server/src/services/correction/dart/add_override.dart';
 import 'package:analysis_server/src/services/correction/dart/convert_add_all_to_spread.dart';
 import 'package:analysis_server/src/services/correction/dart/convert_conditional_expression_to_if_element.dart';
@@ -88,6 +89,7 @@
     LintNames.prefer_adjacent_string_concatenation: RemoveOperator.newInstance,
     LintNames.prefer_conditional_assignment:
         ReplaceWithConditionalAssignment.newInstance,
+    LintNames.prefer_const_constructors_in_immutables: AddConst.newInstance,
     LintNames.prefer_const_declarations: ReplaceFinalWithConst.newInstance,
     LintNames.prefer_contains: ConvertToContains.newInstance,
     LintNames.prefer_equal_for_default_values:
diff --git a/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform_set_error_code.dart b/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform_set_error_code.dart
index 0235247..f44c739 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform_set_error_code.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform_set_error_code.dart
@@ -10,6 +10,16 @@
 class TransformSetErrorCode extends ErrorCode {
   /**
    * Parameters:
+   * 0: the key with which the value is associated
+   * 1: the expected type of the value
+   * 0: the actual type of the value
+   */
+  static const TransformSetErrorCode invalidValue = TransformSetErrorCode(
+      'invalidValue',
+      "The value of '{0}' should be of type '{1}' but is of type '{2}'.");
+
+  /**
+   * Parameters:
    * 0: the unsupported key
    */
   static const TransformSetErrorCode unsupportedKey =
@@ -20,7 +30,7 @@
    * 0: the message produced by the YAML parser
    */
   static const TransformSetErrorCode yamlSyntaxError =
-      TransformSetErrorCode('yamlSyntaxError', "{0}");
+      TransformSetErrorCode('yamlSyntaxError', "Parse error: {0}");
 
   /// Initialize a newly created error code.
   const TransformSetErrorCode(String name, String message,
diff --git a/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform_set_parser.dart b/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform_set_parser.dart
index 19d4808..0690f27 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform_set_parser.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform_set_parser.dart
@@ -81,6 +81,20 @@
     return _translateTransformSet(map);
   }
 
+  /// Return a textual description of the type of value represented by the
+  /// [node].
+  String _nodeType(YamlNode node) {
+    if (node is YamlScalar) {
+      return node.value.runtimeType.toString();
+    } else if (node is YamlList) {
+      return 'List';
+    } else if (node is YamlMap) {
+      return 'Map';
+    }
+    // We shouldn't get here.
+    return node.runtimeType.toString();
+  }
+
   /// Return the result of parsing the file [content] into a YAML node.
   YamlNode _parseYaml(String content) {
     try {
@@ -106,13 +120,14 @@
   /// Report any keys in the [map] whose values are not in [validKeys].
   void _reportUnsupportedKeys(YamlMap map, Set<String> validKeys) {
     for (var keyNode in map.nodes.keys) {
-      if (keyNode is YamlScalar) {
-        var key = _translateString(keyNode);
+      if (keyNode is YamlScalar && keyNode.value is String) {
+        var key = keyNode.value as String;
         if (key != null && !validKeys.contains(key)) {
           _reportError(TransformSetErrorCode.unsupportedKey, keyNode, [key]);
         }
       } else {
-        // TODO(brianwilkerson) Report the unsupported key.
+        // TODO(brianwilkerson) Report the invalidKey.
+        //  "Keys must be of type 'String' but found the type '{0}'."
       }
     }
   }
@@ -127,8 +142,8 @@
     var foundKeys = <String>[];
     var keyToNodeMap = <String, YamlNode>{};
     for (var keyNode in map.nodes.keys) {
-      if (keyNode is YamlScalar) {
-        var key = _translateString(keyNode);
+      if (keyNode is YamlScalar && keyNode.value is String) {
+        var key = keyNode.value as String;
         if (key != null && validKeys.contains(key)) {
           foundKeys.add(key);
           keyToNodeMap[key] = keyNode;
@@ -140,7 +155,7 @@
     }
     for (var i = 1; i < foundKeys.length; i++) {
       // var invalidNode = keyToNodeMap[foundKeys[i]];
-      // TODO(brianwilkerson) Report the invalid key.
+      // TODO(brianwilkerson) Report the problem.
     }
     return foundKeys[0];
   }
@@ -148,18 +163,18 @@
   /// Translate the [node] into an add-type-parameter change. Return the
   /// resulting change, or `null` if the [node] does not represent a valid
   /// add-type-parameter change.
-  Change _translateAddTypeParameterChange(YamlMap node) {
+  AddTypeParameter _translateAddTypeParameterChange(YamlMap node) {
     _reportUnsupportedKeys(
         node, const {_indexKey, _kindKey, _nameKey, _valueKey});
-    var index = _translateInteger(node.valueAt(_indexKey));
+    var index = _translateInteger(node.valueAt(_indexKey), _indexKey);
     if (index == null) {
       return null;
     }
-    var name = _translateString(node.valueAt(_nameKey));
+    var name = _translateString(node.valueAt(_nameKey), _nameKey);
     if (name == null) {
       return null;
     }
-    var value = _translateValueExtractor(node.valueAt(_valueKey));
+    var value = _translateValueExtractor(node.valueAt(_valueKey), _valueKey);
     if (value == null) {
       return null;
     }
@@ -173,7 +188,7 @@
     var indexNode = node.valueAt(_indexKey);
     if (indexNode != null) {
       _reportUnsupportedKeys(node, const {_indexKey, _kindKey});
-      var index = _translateInteger(indexNode);
+      var index = _translateInteger(indexNode, _indexKey);
       if (index == null) {
         // The error has already been reported.
         return null;
@@ -183,7 +198,7 @@
     var nameNode = node.valueAt(_nameKey);
     if (nameNode != null) {
       _reportUnsupportedKeys(node, const {_nameKey, _kindKey});
-      var name = _translateString(nameNode);
+      var name = _translateString(nameNode, _nameKey);
       if (name == null) {
         // The error has already been reported.
         return null;
@@ -195,10 +210,11 @@
   }
 
   /// Translate the [node] into a change. Return the resulting change, or `null`
-  /// if the [node] does not represent a valid change.
-  Change _translateChange(YamlNode node) {
+  /// if the [node] does not represent a valid change. If the [node] is not
+  /// valid, use the name of the associated [key] to report the error.
+  Change _translateChange(YamlNode node, String key) {
     if (node is YamlMap) {
-      var kind = _translateString(node.valueAt(_kindKey));
+      var kind = _translateString(node.valueAt(_kindKey), _kindKey);
       if (kind == _addTypeParameterKind) {
         return _translateAddTypeParameterChange(node);
       } else if (kind == _renameKind) {
@@ -210,21 +226,24 @@
       // TODO(brianwilkerson) Report the missing YAML.
       return null;
     } else {
-      // TODO(brianwilkerson) Report the invalid YAML.
+      _reportError(TransformSetErrorCode.invalidValue, node,
+          [key, 'Map', _nodeType(node)]);
       return null;
     }
   }
 
   /// Translate the [node] into an element descriptor. Return the resulting
   /// descriptor, or `null` if the [node] does not represent a valid element
-  /// descriptor.
-  ElementDescriptor _translateElement(YamlNode node) {
+  /// descriptor. If the [node] is not valid, use the name of the associated
+  /// [key] to report the error.
+  ElementDescriptor _translateElement(YamlNode node, String key) {
     if (node is YamlMap) {
-      var uris = _translateList(node.valueAt(_urisKey), _translateString);
+      var uris =
+          _translateList(node.valueAt(_urisKey), _urisKey, _translateString);
       if (uris == null) {
+        // The error has already been reported.
         // TODO(brianwilkerson) Returning here prevents other errors from being
         //  reported.
-        // The error has already been reported.
         return null;
       }
       var elementKey = _singleKey(node, [
@@ -241,14 +260,15 @@
         _setterKey,
         _typedefKey
       ]);
-      var elementName = _translateString(node.valueAt(elementKey));
+      var elementName = _translateString(node.valueAt(elementKey), elementKey);
       if (elementName == null) {
         // The error has already been reported.
         return null;
       }
       var components = [elementName];
       var containerKey = _singleKey(node, _containerKeyMap[elementKey]);
-      var containerName = _translateString(node.valueAt(containerKey));
+      var containerName =
+          _translateString(node.valueAt(containerKey), containerKey);
       if (containerName == null) {
         if ([_constructorKey, _enumConstantKey, _methodKey, _fieldKey]
             .contains(elementKey)) {
@@ -263,29 +283,31 @@
       // TODO(brianwilkerson) Report the missing YAML.
       return null;
     } else {
-      // TODO(brianwilkerson) Report the invalid YAML.
+      _reportError(TransformSetErrorCode.invalidValue, node,
+          [key, 'Map', _nodeType(node)]);
       return null;
     }
   }
 
   /// Translate the [node] into an integer. Return the resulting integer, or
-  /// `null` if the [node] does not represent a valid integer.
-  int _translateInteger(YamlNode node) {
+  /// `null` if the [node] does not represent a valid integer. If the [node] is
+  /// not valid, use the name of the associated [key] to report the error.
+  int _translateInteger(YamlNode node, String key) {
     if (node is YamlScalar) {
       var value = node.value;
       if (value is int) {
         return value;
       }
-      // TODO(brianwilkerson) Report the invalid YAML. For the best UX we
-      //  probably need to pass in the code to report.
+      _reportError(TransformSetErrorCode.invalidValue, node,
+          [key, 'int', _nodeType(node)]);
       return null;
     } else if (node == null) {
       // TODO(brianwilkerson) Report the missing YAML. For the best UX we
       //  probably need to pass in the code to report.
       return null;
     } else {
-      // TODO(brianwilkerson) Report the invalid YAML. For the best UX we
-      //  probably need to pass in the code to report.
+      _reportError(TransformSetErrorCode.invalidValue, node,
+          [key, 'int', _nodeType(node)]);
       return null;
     }
   }
@@ -293,13 +315,14 @@
   /// Translate the [node] into a list of objects using the [elementTranslator].
   /// Return the resulting list, or `null` if the [node] does not represent a
   /// valid list. If any of the elements of the list can't be translated, they
-  /// will be omitted from the list but the valid elements will be returned.
-  List<R> _translateList<R>(
-      YamlNode node, R Function(YamlNode) elementTranslator) {
+  /// will be omitted from the list, the name of the associated [key] will be
+  /// used to report the error, and the valid elements will be returned.
+  List<R> _translateList<R>(YamlNode node, String key,
+      R Function(YamlNode, String) elementTranslator) {
     if (node is YamlList) {
       var translatedList = <R>[];
       for (var element in node.nodes) {
-        var result = elementTranslator(element);
+        var result = elementTranslator(element, key);
         if (result != null) {
           translatedList.add(result);
         }
@@ -309,16 +332,17 @@
       // TODO(brianwilkerson) Report the missing YAML.
       return null;
     } else {
-      // TODO(brianwilkerson) Report the invalid YAML.
+      _reportError(TransformSetErrorCode.invalidValue, node,
+          [key, 'List', _nodeType(node)]);
       return null;
     }
   }
 
   /// Translate the [node] into a rename change. Return the resulting change, or
   /// `null` if the [node] does not represent a valid rename change.
-  Change _translateRenameChange(YamlMap node) {
+  Rename _translateRenameChange(YamlMap node) {
     _reportUnsupportedKeys(node, const {_kindKey, _newNameKey});
-    var newName = _translateString(node.valueAt(_newNameKey));
+    var newName = _translateString(node.valueAt(_newNameKey), _newNameKey);
     if (newName == null) {
       return null;
     }
@@ -326,36 +350,38 @@
   }
 
   /// Translate the [node] into a string. Return the resulting string, or `null`
-  /// if the [node] does not represent a valid string.
-  String _translateString(YamlNode node) {
+  /// if the [node] does not represent a valid string. If the [node] is not
+  /// valid, use the name of the associated [key] to report the error.
+  String _translateString(YamlNode node, String key) {
     if (node is YamlScalar) {
       var value = node.value;
       if (value is String) {
         return value;
       }
-      // TODO(brianwilkerson) Report the invalid YAML. For the best UX we
-      //  probably need to pass in the code to report.
+      _reportError(TransformSetErrorCode.invalidValue, node,
+          [key, 'String', _nodeType(node)]);
       return null;
     } else if (node == null) {
       // TODO(brianwilkerson) Report the missing YAML. For the best UX we
       //  probably need to pass in the code to report.
       return null;
     } else {
-      // TODO(brianwilkerson) Report the invalid YAML. For the best UX we
-      //  probably need to pass in the code to report.
+      _reportError(TransformSetErrorCode.invalidValue, node,
+          [key, 'String', _nodeType(node)]);
       return null;
     }
   }
 
   /// Translate the [node] into a transform. Return the resulting transform, or
-  /// `null` if the [node] does not represent a valid transform.
-  Transform _translateTransform(YamlNode node) {
+  /// `null` if the [node] does not represent a valid transform. If the [node]
+  /// is not valid, use the name of the associated [key] to report the error.
+  Transform _translateTransform(YamlNode node, String key) {
     if (node is YamlMap) {
       _reportUnsupportedKeys(node, const {_changesKey, _elementKey, _titleKey});
-      var title = _translateString(node.valueAt(_titleKey));
-      var element = _translateElement(node.valueAt(_elementKey));
-      var changes =
-          _translateList<Change>(node.valueAt(_changesKey), _translateChange);
+      var title = _translateString(node.valueAt(_titleKey), _titleKey);
+      var element = _translateElement(node.valueAt(_elementKey), _elementKey);
+      var changes = _translateList<Change>(
+          node.valueAt(_changesKey), _changesKey, _translateChange);
       if (changes == null) {
         // The error has already been reported.
         return null;
@@ -365,7 +391,8 @@
       // TODO(brianwilkerson) Report the missing YAML.
       return null;
     } else {
-      // TODO(brianwilkerson) Report the invalid YAML.
+      _reportError(TransformSetErrorCode.invalidValue, node,
+          [key, 'Map', _nodeType(node)]);
       return null;
     }
   }
@@ -378,12 +405,12 @@
       var set = TransformSet();
       // TODO(brianwilkerson) Version information is currently being ignored,
       //  but needs to be used to select a translator.
-      var version = _translateInteger(node.valueAt(_versionKey));
+      var version = _translateInteger(node.valueAt(_versionKey), _versionKey);
       if (version != currentVersion) {
         // TODO(brianwilkerson) Report the unsupported version.
       }
-      var transformations =
-          _translateList(node.valueAt(_transformsKey), _translateTransform);
+      var transformations = _translateList(
+          node.valueAt(_transformsKey), _transformsKey, _translateTransform);
       if (transformations == null) {
         // The error has already been reported.
         return null;
@@ -396,17 +423,19 @@
       // TODO(brianwilkerson) Report the missing YAML.
       return null;
     } else {
-      // TODO(brianwilkerson) Report the invalid YAML.
+      _reportError(TransformSetErrorCode.invalidValue, node,
+          ['file', 'Map', _nodeType(node)]);
       return null;
     }
   }
 
   /// Translate the [node] into a value extractor. Return the resulting
   /// extractor, or `null` if the [node] does not represent a valid value
-  /// extractor.
-  ValueExtractor _translateValueExtractor(YamlNode node) {
+  /// extractor. If the [node] is not valid, use the name of the associated
+  /// [key] to report the error.
+  ValueExtractor _translateValueExtractor(YamlNode node, String key) {
     if (node is YamlMap) {
-      var kind = _translateString(node.valueAt(_kindKey));
+      var kind = _translateString(node.valueAt(_kindKey), _kindKey);
       if (kind == _argumentKind) {
         return _translateArgumentExtractor(node);
       }
@@ -416,7 +445,8 @@
       // TODO(brianwilkerson) Report the missing YAML.
       return null;
     } else {
-      // TODO(brianwilkerson) Report the invalid YAML.
+      _reportError(TransformSetErrorCode.invalidValue, node,
+          [key, 'Map', _nodeType(node)]);
       return null;
     }
   }
diff --git a/pkg/analysis_server/test/src/services/correction/fix/bulk/add_const_test.dart b/pkg/analysis_server/test/src/services/correction/fix/bulk/add_const_test.dart
new file mode 100644
index 0000000..7962f4f
--- /dev/null
+++ b/pkg/analysis_server/test/src/services/correction/fix/bulk/add_const_test.dart
@@ -0,0 +1,44 @@
+// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:analysis_server/src/services/linter/lint_names.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import 'bulk_fix_processor.dart';
+
+void main() {
+  defineReflectiveSuite(() {
+    defineReflectiveTests(AddConstToImmutableConstructorTest);
+  });
+}
+
+@reflectiveTest
+class AddConstToImmutableConstructorTest extends BulkFixProcessorTest {
+  @override
+  String get lintCode => LintNames.prefer_const_constructors_in_immutables;
+
+  Future<void> test_singleFile() async {
+    addMetaPackage();
+    await resolveTestUnit('''
+import 'package:meta/meta.dart';
+
+@immutable
+class A {
+  A();
+  /// Comment.
+  A.a();
+}
+''');
+    await assertHasFix('''
+import 'package:meta/meta.dart';
+
+@immutable
+class A {
+  const A();
+  /// Comment.
+  const A.a();
+}
+''');
+  }
+}
diff --git a/pkg/analysis_server/test/src/services/correction/fix/bulk/test_all.dart b/pkg/analysis_server/test/src/services/correction/fix/bulk/test_all.dart
index 169e7b5..68dd7a6 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/bulk/test_all.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/bulk/test_all.dart
@@ -5,6 +5,7 @@
 import 'package:test_reflective_loader/test_reflective_loader.dart';
 
 import 'add_await_test.dart' as add_await;
+import 'add_const_test.dart' as add_const;
 import 'add_override_test.dart' as add_override;
 import 'convert_documentation_into_line_test.dart'
     as convert_documentation_into_line;
@@ -56,6 +57,7 @@
 void main() {
   defineReflectiveSuite(() {
     add_await.main();
+    add_const.main();
     add_override.main();
     convert_documentation_into_line.main();
     convert_map_from_iterable_to_for_literal.main();
diff --git a/pkg/analysis_server/test/src/services/correction/fix/data_driven/diagnostics/invalid_value_test.dart b/pkg/analysis_server/test/src/services/correction/fix/data_driven/diagnostics/invalid_value_test.dart
new file mode 100644
index 0000000..2c7d94c
--- /dev/null
+++ b/pkg/analysis_server/test/src/services/correction/fix/data_driven/diagnostics/invalid_value_test.dart
@@ -0,0 +1,161 @@
+// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:analysis_server/src/services/correction/fix/data_driven/transform_set_error_code.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import '../transform_set_parser_test_support.dart';
+
+void main() {
+  defineReflectiveSuite(() {
+    defineReflectiveTests(InvalidValueTest);
+  });
+}
+
+@reflectiveTest
+class InvalidValueTest extends AbstractTransformSetParserTest {
+  void test_change() {
+    assertErrors('''
+version: 1
+transforms:
+- title: 'Rename A'
+  element:
+    uris: ['test.dart']
+    components:
+      - 'A'
+  changes:
+    - 'rename'
+''', [
+      error(TransformSetErrorCode.invalidValue, 123, 8),
+    ]);
+  }
+
+  void test_element() {
+    assertErrors('''
+version: 1
+transforms:
+- title: 'Rename A'
+  element: 5
+  changes:
+    - kind: 'rename'
+      newName: 'B'
+''', [
+      error(TransformSetErrorCode.invalidValue, 54, 1),
+    ]);
+  }
+
+  void test_int_list() {
+    assertErrors('''
+version: []
+transforms:
+- title: 'Rename A'
+  element:
+    uris: ['test.dart']
+    components:
+      - 'A'
+  changes:
+    - kind: 'rename'
+      newName: 'B'
+''', [
+      error(TransformSetErrorCode.invalidValue, 9, 2),
+    ]);
+  }
+
+  void test_int_string() {
+    assertErrors('''
+version: 'first'
+transforms:
+- title: 'Rename A'
+  element:
+    uris: ['test.dart']
+    components:
+      - 'A'
+  changes:
+    - kind: 'rename'
+      newName: 'B'
+''', [
+      error(TransformSetErrorCode.invalidValue, 9, 7),
+    ]);
+  }
+
+  void test_list() {
+    assertErrors('''
+version: 1
+transforms: 3
+''', [
+      error(TransformSetErrorCode.invalidValue, 23, 1),
+    ]);
+  }
+
+  void test_string_int() {
+    assertErrors('''
+version: 1
+transforms:
+- title: 0
+  element:
+    uris: ['test.dart']
+    components:
+      - 'A'
+  changes:
+    - kind: 'rename'
+      newName: 'B'
+''', [
+      error(TransformSetErrorCode.invalidValue, 32, 1),
+    ]);
+  }
+
+  void test_string_list() {
+    assertErrors('''
+version: 1
+transforms:
+- title: []
+  element:
+    uris: ['test.dart']
+    components:
+      - 'A'
+  changes:
+    - kind: 'rename'
+      newName: 'B'
+''', [
+      error(TransformSetErrorCode.invalidValue, 32, 2),
+    ]);
+  }
+
+  void test_transform() {
+    assertErrors('''
+version: 1
+transforms:
+- 'rename'
+''', [
+      error(TransformSetErrorCode.invalidValue, 25, 8),
+    ]);
+  }
+
+  void test_transformSet() {
+    assertErrors('''
+- 'rename'
+''', [
+      error(TransformSetErrorCode.invalidValue, 0, 11),
+    ]);
+  }
+
+  void test_valueExtractor() {
+    assertErrors('''
+version: 1
+transforms:
+- title: 'Rename A'
+  element:
+    uris: ['test.dart']
+    components:
+      - 'A'
+  changes:
+    - kind: 'addTypeParameter'
+      index: 1
+      name: 'T'
+      value: 'int'
+''', [
+      error(TransformSetErrorCode.invalidValue, 192, 5),
+    ]);
+  }
+}
diff --git a/pkg/analysis_server/test/src/services/correction/fix/data_driven/diagnostics/test_all.dart b/pkg/analysis_server/test/src/services/correction/fix/data_driven/diagnostics/test_all.dart
index 6f035e1..a570f51 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/data_driven/diagnostics/test_all.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/data_driven/diagnostics/test_all.dart
@@ -4,11 +4,13 @@
 
 import 'package:test_reflective_loader/test_reflective_loader.dart';
 
+import 'invalid_value_test.dart' as invalid_value;
 import 'unsupported_key_test.dart' as unsupported_key;
 import 'yaml_syntax_error_test.dart' as yaml_syntax_error;
 
 void main() {
   defineReflectiveSuite(() {
+    invalid_value.main();
     unsupported_key.main();
     yaml_syntax_error.main();
   });
diff --git a/pkg/analysis_server/test/src/services/correction/fix/data_driven/transform_set_parser_test.dart b/pkg/analysis_server/test/src/services/correction/fix/data_driven/transform_set_parser_test.dart
index b60500a..58dc538 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/data_driven/transform_set_parser_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/data_driven/transform_set_parser_test.dart
@@ -140,8 +140,9 @@
 transforms:
 ''');
     expect(result, null);
-    // TODO(brianwilkerson) Report a diagnostic.
-    errorListener.assertErrors([]);
+    errorListener.assertErrors([
+      error(TransformSetErrorCode.invalidValue, 21, 0),
+    ]);
   }
 
   void test_invalidYaml() {
diff --git a/pkg/front_end/lib/src/fasta/kernel/kernel_target.dart b/pkg/front_end/lib/src/fasta/kernel/kernel_target.dart
index a7d6a2a..4472d88 100644
--- a/pkg/front_end/lib/src/fasta/kernel/kernel_target.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/kernel_target.dart
@@ -1118,7 +1118,8 @@
 
     if (loader.target.context.options
         .isExperimentEnabledGlobally(ExperimentalFlag.valueClass)) {
-      valueClass.transformComponent(component, loader.coreTypes);
+      valueClass.transformComponent(
+          component, loader.coreTypes, loader.hierarchy);
       ticker.logMs("Lowered value classes");
     }
 
diff --git a/pkg/front_end/testcases/value_class/empty.dart.strong.expect b/pkg/front_end/testcases/value_class/empty.dart.strong.expect
index ae881d2..96207aa 100644
--- a/pkg/front_end/testcases/value_class/empty.dart.strong.expect
+++ b/pkg/front_end/testcases/value_class/empty.dart.strong.expect
@@ -8,6 +8,8 @@
   synthetic constructor •() → self::EmptyClass
     : super core::Object::•()
     ;
+  operator /*isNullableByDefault*/ ==(core::Object other) → core::bool
+    return other is self::EmptyClass;
 }
 static method main() → dynamic {}
 
diff --git a/pkg/front_end/testcases/value_class/empty.dart.strong.transformed.expect b/pkg/front_end/testcases/value_class/empty.dart.strong.transformed.expect
index ae881d2..96207aa 100644
--- a/pkg/front_end/testcases/value_class/empty.dart.strong.transformed.expect
+++ b/pkg/front_end/testcases/value_class/empty.dart.strong.transformed.expect
@@ -8,6 +8,8 @@
   synthetic constructor •() → self::EmptyClass
     : super core::Object::•()
     ;
+  operator /*isNullableByDefault*/ ==(core::Object other) → core::bool
+    return other is self::EmptyClass;
 }
 static method main() → dynamic {}
 
diff --git a/pkg/front_end/testcases/value_class/empty.dart.weak.expect b/pkg/front_end/testcases/value_class/empty.dart.weak.expect
index ae881d2..96207aa 100644
--- a/pkg/front_end/testcases/value_class/empty.dart.weak.expect
+++ b/pkg/front_end/testcases/value_class/empty.dart.weak.expect
@@ -8,6 +8,8 @@
   synthetic constructor •() → self::EmptyClass
     : super core::Object::•()
     ;
+  operator /*isNullableByDefault*/ ==(core::Object other) → core::bool
+    return other is self::EmptyClass;
 }
 static method main() → dynamic {}
 
diff --git a/pkg/front_end/testcases/value_class/empty.dart.weak.transformed.expect b/pkg/front_end/testcases/value_class/empty.dart.weak.transformed.expect
index ae881d2..96207aa 100644
--- a/pkg/front_end/testcases/value_class/empty.dart.weak.transformed.expect
+++ b/pkg/front_end/testcases/value_class/empty.dart.weak.transformed.expect
@@ -8,6 +8,8 @@
   synthetic constructor •() → self::EmptyClass
     : super core::Object::•()
     ;
+  operator /*isNullableByDefault*/ ==(core::Object other) → core::bool
+    return other is self::EmptyClass;
 }
 static method main() → dynamic {}
 
diff --git a/pkg/front_end/testcases/value_class/explicit_mixin.dart.strong.expect b/pkg/front_end/testcases/value_class/explicit_mixin.dart.strong.expect
index 028ea9c..9fe3384 100644
--- a/pkg/front_end/testcases/value_class/explicit_mixin.dart.strong.expect
+++ b/pkg/front_end/testcases/value_class/explicit_mixin.dart.strong.expect
@@ -8,6 +8,8 @@
   synthetic constructor •() → self::A
     : super core::Object::•()
     ;
+  operator /*isNullableByDefault*/ ==(core::Object other) → core::bool
+    return other is self::A;
 }
 class B extends core::Object {
   synthetic constructor •() → self::B
@@ -33,6 +35,8 @@
   synthetic constructor •() → self::F
     : super self::B::•()
     ;
+  operator /*isNullableByDefault*/ ==(core::Object other) → core::bool
+    return other is self::F;
 }
 static method main() → dynamic {}
 
diff --git a/pkg/front_end/testcases/value_class/explicit_mixin.dart.strong.transformed.expect b/pkg/front_end/testcases/value_class/explicit_mixin.dart.strong.transformed.expect
index 748d548..fa46235 100644
--- a/pkg/front_end/testcases/value_class/explicit_mixin.dart.strong.transformed.expect
+++ b/pkg/front_end/testcases/value_class/explicit_mixin.dart.strong.transformed.expect
@@ -8,6 +8,8 @@
   synthetic constructor •() → self::A
     : super core::Object::•()
     ;
+  operator /*isNullableByDefault*/ ==(core::Object other) → core::bool
+    return other is self::A;
 }
 class B extends core::Object {
   synthetic constructor •() → self::B
@@ -28,11 +30,15 @@
   synthetic constructor •() → self::E
     : super self::B::•()
     ;
+  operator /*isNullableByDefault*/ ==(core::Object other) → core::bool
+    return other is self::A;
 }
 class F extends self::B implements self::C /*isEliminatedMixin*/  {
   synthetic constructor •() → self::F
     : super self::B::•()
     ;
+  operator /*isNullableByDefault*/ ==(core::Object other) → core::bool
+    return other is self::F;
 }
 static method main() → dynamic {}
 
diff --git a/pkg/front_end/testcases/value_class/explicit_mixin.dart.weak.expect b/pkg/front_end/testcases/value_class/explicit_mixin.dart.weak.expect
index 028ea9c..9fe3384 100644
--- a/pkg/front_end/testcases/value_class/explicit_mixin.dart.weak.expect
+++ b/pkg/front_end/testcases/value_class/explicit_mixin.dart.weak.expect
@@ -8,6 +8,8 @@
   synthetic constructor •() → self::A
     : super core::Object::•()
     ;
+  operator /*isNullableByDefault*/ ==(core::Object other) → core::bool
+    return other is self::A;
 }
 class B extends core::Object {
   synthetic constructor •() → self::B
@@ -33,6 +35,8 @@
   synthetic constructor •() → self::F
     : super self::B::•()
     ;
+  operator /*isNullableByDefault*/ ==(core::Object other) → core::bool
+    return other is self::F;
 }
 static method main() → dynamic {}
 
diff --git a/pkg/front_end/testcases/value_class/explicit_mixin.dart.weak.transformed.expect b/pkg/front_end/testcases/value_class/explicit_mixin.dart.weak.transformed.expect
index 748d548..fa46235 100644
--- a/pkg/front_end/testcases/value_class/explicit_mixin.dart.weak.transformed.expect
+++ b/pkg/front_end/testcases/value_class/explicit_mixin.dart.weak.transformed.expect
@@ -8,6 +8,8 @@
   synthetic constructor •() → self::A
     : super core::Object::•()
     ;
+  operator /*isNullableByDefault*/ ==(core::Object other) → core::bool
+    return other is self::A;
 }
 class B extends core::Object {
   synthetic constructor •() → self::B
@@ -28,11 +30,15 @@
   synthetic constructor •() → self::E
     : super self::B::•()
     ;
+  operator /*isNullableByDefault*/ ==(core::Object other) → core::bool
+    return other is self::A;
 }
 class F extends self::B implements self::C /*isEliminatedMixin*/  {
   synthetic constructor •() → self::F
     : super self::B::•()
     ;
+  operator /*isNullableByDefault*/ ==(core::Object other) → core::bool
+    return other is self::F;
 }
 static method main() → dynamic {}
 
diff --git a/pkg/front_end/testcases/value_class/non_final_field_error.dart.strong.expect b/pkg/front_end/testcases/value_class/non_final_field_error.dart.strong.expect
index 8cacedc..fa2785e 100644
--- a/pkg/front_end/testcases/value_class/non_final_field_error.dart.strong.expect
+++ b/pkg/front_end/testcases/value_class/non_final_field_error.dart.strong.expect
@@ -16,6 +16,8 @@
   synthetic constructor •({required core::int numberOfLegs}) → self::Animal
     : self::Animal::numberOfLegs = numberOfLegs, super core::Object::•()
     ;
+  operator /*isNullableByDefault*/ ==(core::Object other) → core::bool
+    return other is self::Animal && this.{self::Animal::numberOfLegs}.{core::num::==}(other{self::Animal}.{self::Animal::numberOfLegs});
 }
 static method main() → dynamic {}
 
diff --git a/pkg/front_end/testcases/value_class/non_final_field_error.dart.strong.transformed.expect b/pkg/front_end/testcases/value_class/non_final_field_error.dart.strong.transformed.expect
index 8cacedc..fa2785e 100644
--- a/pkg/front_end/testcases/value_class/non_final_field_error.dart.strong.transformed.expect
+++ b/pkg/front_end/testcases/value_class/non_final_field_error.dart.strong.transformed.expect
@@ -16,6 +16,8 @@
   synthetic constructor •({required core::int numberOfLegs}) → self::Animal
     : self::Animal::numberOfLegs = numberOfLegs, super core::Object::•()
     ;
+  operator /*isNullableByDefault*/ ==(core::Object other) → core::bool
+    return other is self::Animal && this.{self::Animal::numberOfLegs}.{core::num::==}(other{self::Animal}.{self::Animal::numberOfLegs});
 }
 static method main() → dynamic {}
 
diff --git a/pkg/front_end/testcases/value_class/non_final_field_error.dart.weak.expect b/pkg/front_end/testcases/value_class/non_final_field_error.dart.weak.expect
index 8cacedc..fa2785e 100644
--- a/pkg/front_end/testcases/value_class/non_final_field_error.dart.weak.expect
+++ b/pkg/front_end/testcases/value_class/non_final_field_error.dart.weak.expect
@@ -16,6 +16,8 @@
   synthetic constructor •({required core::int numberOfLegs}) → self::Animal
     : self::Animal::numberOfLegs = numberOfLegs, super core::Object::•()
     ;
+  operator /*isNullableByDefault*/ ==(core::Object other) → core::bool
+    return other is self::Animal && this.{self::Animal::numberOfLegs}.{core::num::==}(other{self::Animal}.{self::Animal::numberOfLegs});
 }
 static method main() → dynamic {}
 
diff --git a/pkg/front_end/testcases/value_class/non_final_field_error.dart.weak.transformed.expect b/pkg/front_end/testcases/value_class/non_final_field_error.dart.weak.transformed.expect
index 8cacedc..fa2785e 100644
--- a/pkg/front_end/testcases/value_class/non_final_field_error.dart.weak.transformed.expect
+++ b/pkg/front_end/testcases/value_class/non_final_field_error.dart.weak.transformed.expect
@@ -16,6 +16,8 @@
   synthetic constructor •({required core::int numberOfLegs}) → self::Animal
     : self::Animal::numberOfLegs = numberOfLegs, super core::Object::•()
     ;
+  operator /*isNullableByDefault*/ ==(core::Object other) → core::bool
+    return other is self::Animal && this.{self::Animal::numberOfLegs}.{core::num::==}(other{self::Animal}.{self::Animal::numberOfLegs});
 }
 static method main() → dynamic {}
 
diff --git a/pkg/front_end/testcases/value_class/non_value_extends_value_error.dart.strong.expect b/pkg/front_end/testcases/value_class/non_value_extends_value_error.dart.strong.expect
index 60b3565..a5978c5 100644
--- a/pkg/front_end/testcases/value_class/non_value_extends_value_error.dart.strong.expect
+++ b/pkg/front_end/testcases/value_class/non_value_extends_value_error.dart.strong.expect
@@ -17,6 +17,8 @@
   synthetic constructor •({required core::int numberOfLegs}) → self::Animal
     : self::Animal::numberOfLegs = numberOfLegs, super core::Object::•()
     ;
+  operator /*isNullableByDefault*/ ==(core::Object other) → core::bool
+    return other is self::Animal && this.{self::Animal::numberOfLegs}.{core::num::==}(other{self::Animal}.{self::Animal::numberOfLegs});
 }
 class Cat extends self::Animal {
   synthetic constructor •() → self::Cat
diff --git a/pkg/front_end/testcases/value_class/non_value_extends_value_error.dart.strong.transformed.expect b/pkg/front_end/testcases/value_class/non_value_extends_value_error.dart.strong.transformed.expect
index 60b3565..a5978c5 100644
--- a/pkg/front_end/testcases/value_class/non_value_extends_value_error.dart.strong.transformed.expect
+++ b/pkg/front_end/testcases/value_class/non_value_extends_value_error.dart.strong.transformed.expect
@@ -17,6 +17,8 @@
   synthetic constructor •({required core::int numberOfLegs}) → self::Animal
     : self::Animal::numberOfLegs = numberOfLegs, super core::Object::•()
     ;
+  operator /*isNullableByDefault*/ ==(core::Object other) → core::bool
+    return other is self::Animal && this.{self::Animal::numberOfLegs}.{core::num::==}(other{self::Animal}.{self::Animal::numberOfLegs});
 }
 class Cat extends self::Animal {
   synthetic constructor •() → self::Cat
diff --git a/pkg/front_end/testcases/value_class/non_value_extends_value_error.dart.weak.expect b/pkg/front_end/testcases/value_class/non_value_extends_value_error.dart.weak.expect
index 60b3565..a5978c5 100644
--- a/pkg/front_end/testcases/value_class/non_value_extends_value_error.dart.weak.expect
+++ b/pkg/front_end/testcases/value_class/non_value_extends_value_error.dart.weak.expect
@@ -17,6 +17,8 @@
   synthetic constructor •({required core::int numberOfLegs}) → self::Animal
     : self::Animal::numberOfLegs = numberOfLegs, super core::Object::•()
     ;
+  operator /*isNullableByDefault*/ ==(core::Object other) → core::bool
+    return other is self::Animal && this.{self::Animal::numberOfLegs}.{core::num::==}(other{self::Animal}.{self::Animal::numberOfLegs});
 }
 class Cat extends self::Animal {
   synthetic constructor •() → self::Cat
diff --git a/pkg/front_end/testcases/value_class/non_value_extends_value_error.dart.weak.transformed.expect b/pkg/front_end/testcases/value_class/non_value_extends_value_error.dart.weak.transformed.expect
index 60b3565..a5978c5 100644
--- a/pkg/front_end/testcases/value_class/non_value_extends_value_error.dart.weak.transformed.expect
+++ b/pkg/front_end/testcases/value_class/non_value_extends_value_error.dart.weak.transformed.expect
@@ -17,6 +17,8 @@
   synthetic constructor •({required core::int numberOfLegs}) → self::Animal
     : self::Animal::numberOfLegs = numberOfLegs, super core::Object::•()
     ;
+  operator /*isNullableByDefault*/ ==(core::Object other) → core::bool
+    return other is self::Animal && this.{self::Animal::numberOfLegs}.{core::num::==}(other{self::Animal}.{self::Animal::numberOfLegs});
 }
 class Cat extends self::Animal {
   synthetic constructor •() → self::Cat
diff --git a/pkg/front_end/testcases/value_class/non_value_implements_value_error.dart.strong.expect b/pkg/front_end/testcases/value_class/non_value_implements_value_error.dart.strong.expect
index 121eef2..dc47c2a 100644
--- a/pkg/front_end/testcases/value_class/non_value_implements_value_error.dart.strong.expect
+++ b/pkg/front_end/testcases/value_class/non_value_implements_value_error.dart.strong.expect
@@ -22,6 +22,8 @@
   synthetic constructor •({required core::int numberOfLegs}) → self::Animal
     : self::Animal::numberOfLegs = numberOfLegs, super core::Object::•()
     ;
+  operator /*isNullableByDefault*/ ==(core::Object other) → core::bool
+    return other is self::Animal && this.{self::Animal::numberOfLegs}.{core::num::==}(other{self::Animal}.{self::Animal::numberOfLegs});
 }
 class Cat extends core::Object implements self::Animal {
   final field core::int numberOfLegs = null;
diff --git a/pkg/front_end/testcases/value_class/non_value_implements_value_error.dart.strong.transformed.expect b/pkg/front_end/testcases/value_class/non_value_implements_value_error.dart.strong.transformed.expect
index 121eef2..dc47c2a 100644
--- a/pkg/front_end/testcases/value_class/non_value_implements_value_error.dart.strong.transformed.expect
+++ b/pkg/front_end/testcases/value_class/non_value_implements_value_error.dart.strong.transformed.expect
@@ -22,6 +22,8 @@
   synthetic constructor •({required core::int numberOfLegs}) → self::Animal
     : self::Animal::numberOfLegs = numberOfLegs, super core::Object::•()
     ;
+  operator /*isNullableByDefault*/ ==(core::Object other) → core::bool
+    return other is self::Animal && this.{self::Animal::numberOfLegs}.{core::num::==}(other{self::Animal}.{self::Animal::numberOfLegs});
 }
 class Cat extends core::Object implements self::Animal {
   final field core::int numberOfLegs = null;
diff --git a/pkg/front_end/testcases/value_class/non_value_implements_value_error.dart.weak.expect b/pkg/front_end/testcases/value_class/non_value_implements_value_error.dart.weak.expect
index 121eef2..dc47c2a 100644
--- a/pkg/front_end/testcases/value_class/non_value_implements_value_error.dart.weak.expect
+++ b/pkg/front_end/testcases/value_class/non_value_implements_value_error.dart.weak.expect
@@ -22,6 +22,8 @@
   synthetic constructor •({required core::int numberOfLegs}) → self::Animal
     : self::Animal::numberOfLegs = numberOfLegs, super core::Object::•()
     ;
+  operator /*isNullableByDefault*/ ==(core::Object other) → core::bool
+    return other is self::Animal && this.{self::Animal::numberOfLegs}.{core::num::==}(other{self::Animal}.{self::Animal::numberOfLegs});
 }
 class Cat extends core::Object implements self::Animal {
   final field core::int numberOfLegs = null;
diff --git a/pkg/front_end/testcases/value_class/non_value_implements_value_error.dart.weak.transformed.expect b/pkg/front_end/testcases/value_class/non_value_implements_value_error.dart.weak.transformed.expect
index 121eef2..dc47c2a 100644
--- a/pkg/front_end/testcases/value_class/non_value_implements_value_error.dart.weak.transformed.expect
+++ b/pkg/front_end/testcases/value_class/non_value_implements_value_error.dart.weak.transformed.expect
@@ -22,6 +22,8 @@
   synthetic constructor •({required core::int numberOfLegs}) → self::Animal
     : self::Animal::numberOfLegs = numberOfLegs, super core::Object::•()
     ;
+  operator /*isNullableByDefault*/ ==(core::Object other) → core::bool
+    return other is self::Animal && this.{self::Animal::numberOfLegs}.{core::num::==}(other{self::Animal}.{self::Animal::numberOfLegs});
 }
 class Cat extends core::Object implements self::Animal {
   final field core::int numberOfLegs = null;
diff --git a/pkg/front_end/testcases/value_class/simple.dart.strong.expect b/pkg/front_end/testcases/value_class/simple.dart.strong.expect
index 49fa098..eb9f0d0 100644
--- a/pkg/front_end/testcases/value_class/simple.dart.strong.expect
+++ b/pkg/front_end/testcases/value_class/simple.dart.strong.expect
@@ -38,6 +38,8 @@
   synthetic constructor •({required core::int numberOfLegs}) → self::Animal
     : self::Animal::numberOfLegs = numberOfLegs, super core::Object::•()
     ;
+  operator /*isNullableByDefault*/ ==(core::Object other) → core::bool
+    return other is self::Animal && this.{self::Animal::numberOfLegs}.{core::num::==}(other{self::Animal}.{self::Animal::numberOfLegs});
 }
 static method main() → dynamic {
   self::Animal firstAnimal = invalid-expression "pkg/front_end/testcases/value_class/simple.dart:13:31: Error: No named parameter with the name 'numberOfLegs'.
diff --git a/pkg/front_end/testcases/value_class/simple.dart.strong.transformed.expect b/pkg/front_end/testcases/value_class/simple.dart.strong.transformed.expect
index 178b102..ea02301 100644
--- a/pkg/front_end/testcases/value_class/simple.dart.strong.transformed.expect
+++ b/pkg/front_end/testcases/value_class/simple.dart.strong.transformed.expect
@@ -38,6 +38,8 @@
   synthetic constructor •({required core::int numberOfLegs}) → self::Animal
     : self::Animal::numberOfLegs = numberOfLegs, super core::Object::•()
     ;
+  operator /*isNullableByDefault*/ ==(core::Object other) → core::bool
+    return other is self::Animal && this.{self::Animal::numberOfLegs}.{core::num::==}(other{self::Animal}.{self::Animal::numberOfLegs});
 }
 static method main() → dynamic {
   self::Animal firstAnimal = invalid-expression "pkg/front_end/testcases/value_class/simple.dart:13:31: Error: No named parameter with the name 'numberOfLegs'.
diff --git a/pkg/front_end/testcases/value_class/simple.dart.weak.expect b/pkg/front_end/testcases/value_class/simple.dart.weak.expect
index 49fa098..eb9f0d0 100644
--- a/pkg/front_end/testcases/value_class/simple.dart.weak.expect
+++ b/pkg/front_end/testcases/value_class/simple.dart.weak.expect
@@ -38,6 +38,8 @@
   synthetic constructor •({required core::int numberOfLegs}) → self::Animal
     : self::Animal::numberOfLegs = numberOfLegs, super core::Object::•()
     ;
+  operator /*isNullableByDefault*/ ==(core::Object other) → core::bool
+    return other is self::Animal && this.{self::Animal::numberOfLegs}.{core::num::==}(other{self::Animal}.{self::Animal::numberOfLegs});
 }
 static method main() → dynamic {
   self::Animal firstAnimal = invalid-expression "pkg/front_end/testcases/value_class/simple.dart:13:31: Error: No named parameter with the name 'numberOfLegs'.
diff --git a/pkg/front_end/testcases/value_class/simple.dart.weak.transformed.expect b/pkg/front_end/testcases/value_class/simple.dart.weak.transformed.expect
index 178b102..ea02301 100644
--- a/pkg/front_end/testcases/value_class/simple.dart.weak.transformed.expect
+++ b/pkg/front_end/testcases/value_class/simple.dart.weak.transformed.expect
@@ -38,6 +38,8 @@
   synthetic constructor •({required core::int numberOfLegs}) → self::Animal
     : self::Animal::numberOfLegs = numberOfLegs, super core::Object::•()
     ;
+  operator /*isNullableByDefault*/ ==(core::Object other) → core::bool
+    return other is self::Animal && this.{self::Animal::numberOfLegs}.{core::num::==}(other{self::Animal}.{self::Animal::numberOfLegs});
 }
 static method main() → dynamic {
   self::Animal firstAnimal = invalid-expression "pkg/front_end/testcases/value_class/simple.dart:13:31: Error: No named parameter with the name 'numberOfLegs'.
diff --git a/pkg/front_end/testcases/value_class/value_extends_non_value.dart.strong.expect b/pkg/front_end/testcases/value_class/value_extends_non_value.dart.strong.expect
index d1dfe1d..aa94e2f 100644
--- a/pkg/front_end/testcases/value_class/value_extends_non_value.dart.strong.expect
+++ b/pkg/front_end/testcases/value_class/value_extends_non_value.dart.strong.expect
@@ -44,6 +44,8 @@
   synthetic constructor •({required core::int numberOfWhiskers, core::int numberOfLegs}) → self::Cat
     : self::Cat::numberOfWhiskers = numberOfWhiskers, super self::Animal::•(numberOfLegs)
     ;
+  operator /*isNullableByDefault*/ ==(core::Object other) → core::bool
+    return other is self::Cat && this.{self::Animal::numberOfLegs}.{core::num::==}(other{self::Cat}.{self::Animal::numberOfLegs}) && this.{self::Cat::numberOfWhiskers}.{core::num::==}(other{self::Cat}.{self::Cat::numberOfWhiskers});
 }
 static method main() → dynamic {
   self::Cat firstCat = invalid-expression "pkg/front_end/testcases/value_class/value_extends_non_value.dart:18:22: Error: No named parameter with the name 'numberOfLegs'.
diff --git a/pkg/front_end/testcases/value_class/value_extends_non_value.dart.strong.transformed.expect b/pkg/front_end/testcases/value_class/value_extends_non_value.dart.strong.transformed.expect
index 0e71392..0a9d8aa 100644
--- a/pkg/front_end/testcases/value_class/value_extends_non_value.dart.strong.transformed.expect
+++ b/pkg/front_end/testcases/value_class/value_extends_non_value.dart.strong.transformed.expect
@@ -44,6 +44,8 @@
   synthetic constructor •({required core::int numberOfWhiskers, core::int numberOfLegs}) → self::Cat
     : self::Cat::numberOfWhiskers = numberOfWhiskers, super self::Animal::•(numberOfLegs)
     ;
+  operator /*isNullableByDefault*/ ==(core::Object other) → core::bool
+    return other is self::Cat && this.{self::Animal::numberOfLegs}.{core::num::==}(other{self::Cat}.{self::Animal::numberOfLegs}) && this.{self::Cat::numberOfWhiskers}.{core::num::==}(other{self::Cat}.{self::Cat::numberOfWhiskers});
 }
 static method main() → dynamic {
   self::Cat firstCat = invalid-expression "pkg/front_end/testcases/value_class/value_extends_non_value.dart:18:22: Error: No named parameter with the name 'numberOfLegs'.
diff --git a/pkg/front_end/testcases/value_class/value_extends_non_value.dart.weak.expect b/pkg/front_end/testcases/value_class/value_extends_non_value.dart.weak.expect
index d1dfe1d..aa94e2f 100644
--- a/pkg/front_end/testcases/value_class/value_extends_non_value.dart.weak.expect
+++ b/pkg/front_end/testcases/value_class/value_extends_non_value.dart.weak.expect
@@ -44,6 +44,8 @@
   synthetic constructor •({required core::int numberOfWhiskers, core::int numberOfLegs}) → self::Cat
     : self::Cat::numberOfWhiskers = numberOfWhiskers, super self::Animal::•(numberOfLegs)
     ;
+  operator /*isNullableByDefault*/ ==(core::Object other) → core::bool
+    return other is self::Cat && this.{self::Animal::numberOfLegs}.{core::num::==}(other{self::Cat}.{self::Animal::numberOfLegs}) && this.{self::Cat::numberOfWhiskers}.{core::num::==}(other{self::Cat}.{self::Cat::numberOfWhiskers});
 }
 static method main() → dynamic {
   self::Cat firstCat = invalid-expression "pkg/front_end/testcases/value_class/value_extends_non_value.dart:18:22: Error: No named parameter with the name 'numberOfLegs'.
diff --git a/pkg/front_end/testcases/value_class/value_extends_non_value.dart.weak.transformed.expect b/pkg/front_end/testcases/value_class/value_extends_non_value.dart.weak.transformed.expect
index 0e71392..0a9d8aa 100644
--- a/pkg/front_end/testcases/value_class/value_extends_non_value.dart.weak.transformed.expect
+++ b/pkg/front_end/testcases/value_class/value_extends_non_value.dart.weak.transformed.expect
@@ -44,6 +44,8 @@
   synthetic constructor •({required core::int numberOfWhiskers, core::int numberOfLegs}) → self::Cat
     : self::Cat::numberOfWhiskers = numberOfWhiskers, super self::Animal::•(numberOfLegs)
     ;
+  operator /*isNullableByDefault*/ ==(core::Object other) → core::bool
+    return other is self::Cat && this.{self::Animal::numberOfLegs}.{core::num::==}(other{self::Cat}.{self::Animal::numberOfLegs}) && this.{self::Cat::numberOfWhiskers}.{core::num::==}(other{self::Cat}.{self::Cat::numberOfWhiskers});
 }
 static method main() → dynamic {
   self::Cat firstCat = invalid-expression "pkg/front_end/testcases/value_class/value_extends_non_value.dart:18:22: Error: No named parameter with the name 'numberOfLegs'.
diff --git a/pkg/front_end/testcases/value_class/value_extends_non_value_error.dart.strong.expect b/pkg/front_end/testcases/value_class/value_extends_non_value_error.dart.strong.expect
index 48ba960..e0bd837 100644
--- a/pkg/front_end/testcases/value_class/value_extends_non_value_error.dart.strong.expect
+++ b/pkg/front_end/testcases/value_class/value_extends_non_value_error.dart.strong.expect
@@ -26,6 +26,8 @@
   synthetic constructor •() → self::Cat
     : super self::Animal::•()
     ;
+  operator /*isNullableByDefault*/ ==(core::Object other) → core::bool
+    return other is self::Cat;
 }
 class Animal2 extends core::Object {
   final field core::int numberOfLegs = null;
@@ -37,6 +39,8 @@
   synthetic constructor •() → self::Cat2
     : super self::Animal2::•()
     ;
+  operator /*isNullableByDefault*/ ==(core::Object other) → core::bool
+    return other is self::Cat2;
 }
 static method main() → dynamic {}
 
diff --git a/pkg/front_end/testcases/value_class/value_extends_non_value_error.dart.strong.transformed.expect b/pkg/front_end/testcases/value_class/value_extends_non_value_error.dart.strong.transformed.expect
index 48ba960..e0bd837 100644
--- a/pkg/front_end/testcases/value_class/value_extends_non_value_error.dart.strong.transformed.expect
+++ b/pkg/front_end/testcases/value_class/value_extends_non_value_error.dart.strong.transformed.expect
@@ -26,6 +26,8 @@
   synthetic constructor •() → self::Cat
     : super self::Animal::•()
     ;
+  operator /*isNullableByDefault*/ ==(core::Object other) → core::bool
+    return other is self::Cat;
 }
 class Animal2 extends core::Object {
   final field core::int numberOfLegs = null;
@@ -37,6 +39,8 @@
   synthetic constructor •() → self::Cat2
     : super self::Animal2::•()
     ;
+  operator /*isNullableByDefault*/ ==(core::Object other) → core::bool
+    return other is self::Cat2;
 }
 static method main() → dynamic {}
 
diff --git a/pkg/front_end/testcases/value_class/value_extends_non_value_error.dart.weak.expect b/pkg/front_end/testcases/value_class/value_extends_non_value_error.dart.weak.expect
index 48ba960..e0bd837 100644
--- a/pkg/front_end/testcases/value_class/value_extends_non_value_error.dart.weak.expect
+++ b/pkg/front_end/testcases/value_class/value_extends_non_value_error.dart.weak.expect
@@ -26,6 +26,8 @@
   synthetic constructor •() → self::Cat
     : super self::Animal::•()
     ;
+  operator /*isNullableByDefault*/ ==(core::Object other) → core::bool
+    return other is self::Cat;
 }
 class Animal2 extends core::Object {
   final field core::int numberOfLegs = null;
@@ -37,6 +39,8 @@
   synthetic constructor •() → self::Cat2
     : super self::Animal2::•()
     ;
+  operator /*isNullableByDefault*/ ==(core::Object other) → core::bool
+    return other is self::Cat2;
 }
 static method main() → dynamic {}
 
diff --git a/pkg/front_end/testcases/value_class/value_extends_non_value_error.dart.weak.transformed.expect b/pkg/front_end/testcases/value_class/value_extends_non_value_error.dart.weak.transformed.expect
index 48ba960..e0bd837 100644
--- a/pkg/front_end/testcases/value_class/value_extends_non_value_error.dart.weak.transformed.expect
+++ b/pkg/front_end/testcases/value_class/value_extends_non_value_error.dart.weak.transformed.expect
@@ -26,6 +26,8 @@
   synthetic constructor •() → self::Cat
     : super self::Animal::•()
     ;
+  operator /*isNullableByDefault*/ ==(core::Object other) → core::bool
+    return other is self::Cat;
 }
 class Animal2 extends core::Object {
   final field core::int numberOfLegs = null;
@@ -37,6 +39,8 @@
   synthetic constructor •() → self::Cat2
     : super self::Animal2::•()
     ;
+  operator /*isNullableByDefault*/ ==(core::Object other) → core::bool
+    return other is self::Cat2;
 }
 static method main() → dynamic {}
 
diff --git a/pkg/front_end/testcases/value_class/value_implements_non_value.dart.strong.expect b/pkg/front_end/testcases/value_class/value_implements_non_value.dart.strong.expect
index 9673b1e..28a8793 100644
--- a/pkg/front_end/testcases/value_class/value_implements_non_value.dart.strong.expect
+++ b/pkg/front_end/testcases/value_class/value_implements_non_value.dart.strong.expect
@@ -87,6 +87,8 @@
   synthetic constructor •({required core::int numberOfLegs, required core::int numberOfWhiskers}) → self::Cat
     : self::Cat::numberOfLegs = numberOfLegs, self::Cat::numberOfWhiskers = numberOfWhiskers, super core::Object::•()
     ;
+  operator /*isNullableByDefault*/ ==(core::Object other) → core::bool
+    return other is self::Cat && this.{self::Cat::numberOfLegs}.{core::num::==}(other{self::Cat}.{self::Cat::numberOfLegs}) && this.{self::Cat::numberOfWhiskers}.{core::num::==}(other{self::Cat}.{self::Cat::numberOfWhiskers});
 }
 abstract class Animal2 extends core::Object {
   synthetic constructor •() → self::Animal2
@@ -100,6 +102,8 @@
   synthetic constructor •({required core::int numberOfLegs, required core::int numberOfWhiskers}) → self::Cat2
     : self::Cat2::numberOfLegs = numberOfLegs, self::Cat2::numberOfWhiskers = numberOfWhiskers, super core::Object::•()
     ;
+  operator /*isNullableByDefault*/ ==(core::Object other) → core::bool
+    return other is self::Cat2 && this.{self::Cat2::numberOfLegs}.{core::num::==}(other{self::Cat2}.{self::Cat2::numberOfLegs}) && this.{self::Cat2::numberOfWhiskers}.{core::num::==}(other{self::Cat2}.{self::Cat2::numberOfWhiskers});
 }
 static method main() → dynamic {
   self::Cat firstCat = invalid-expression "pkg/front_end/testcases/value_class/value_implements_non_value.dart:30:22: Error: No named parameter with the name 'numberOfLegs'.
diff --git a/pkg/front_end/testcases/value_class/value_implements_non_value.dart.strong.transformed.expect b/pkg/front_end/testcases/value_class/value_implements_non_value.dart.strong.transformed.expect
index bc62310..4a66e5d 100644
--- a/pkg/front_end/testcases/value_class/value_implements_non_value.dart.strong.transformed.expect
+++ b/pkg/front_end/testcases/value_class/value_implements_non_value.dart.strong.transformed.expect
@@ -87,6 +87,8 @@
   synthetic constructor •({required core::int numberOfLegs, required core::int numberOfWhiskers}) → self::Cat
     : self::Cat::numberOfLegs = numberOfLegs, self::Cat::numberOfWhiskers = numberOfWhiskers, super core::Object::•()
     ;
+  operator /*isNullableByDefault*/ ==(core::Object other) → core::bool
+    return other is self::Cat && this.{self::Cat::numberOfLegs}.{core::num::==}(other{self::Cat}.{self::Cat::numberOfLegs}) && this.{self::Cat::numberOfWhiskers}.{core::num::==}(other{self::Cat}.{self::Cat::numberOfWhiskers});
 }
 abstract class Animal2 extends core::Object {
   synthetic constructor •() → self::Animal2
@@ -100,6 +102,8 @@
   synthetic constructor •({required core::int numberOfLegs, required core::int numberOfWhiskers}) → self::Cat2
     : self::Cat2::numberOfLegs = numberOfLegs, self::Cat2::numberOfWhiskers = numberOfWhiskers, super core::Object::•()
     ;
+  operator /*isNullableByDefault*/ ==(core::Object other) → core::bool
+    return other is self::Cat2 && this.{self::Cat2::numberOfLegs}.{core::num::==}(other{self::Cat2}.{self::Cat2::numberOfLegs}) && this.{self::Cat2::numberOfWhiskers}.{core::num::==}(other{self::Cat2}.{self::Cat2::numberOfWhiskers});
 }
 static method main() → dynamic {
   self::Cat firstCat = invalid-expression "pkg/front_end/testcases/value_class/value_implements_non_value.dart:30:22: Error: No named parameter with the name 'numberOfLegs'.
diff --git a/pkg/front_end/testcases/value_class/value_implements_non_value.dart.weak.expect b/pkg/front_end/testcases/value_class/value_implements_non_value.dart.weak.expect
index b0891a3..21d4e29 100644
--- a/pkg/front_end/testcases/value_class/value_implements_non_value.dart.weak.expect
+++ b/pkg/front_end/testcases/value_class/value_implements_non_value.dart.weak.expect
@@ -87,6 +87,8 @@
   synthetic constructor •({required core::int numberOfLegs, required core::int numberOfWhiskers}) → self::Cat
     : self::Cat::numberOfLegs = numberOfLegs, self::Cat::numberOfWhiskers = numberOfWhiskers, super core::Object::•()
     ;
+  operator /*isNullableByDefault*/ ==(core::Object other) → core::bool
+    return other is self::Cat && this.{self::Cat::numberOfLegs}.{core::num::==}(other{self::Cat}.{self::Cat::numberOfLegs}) && this.{self::Cat::numberOfWhiskers}.{core::num::==}(other{self::Cat}.{self::Cat::numberOfWhiskers});
 }
 abstract class Animal2 extends core::Object {
   synthetic constructor •() → self::Animal2
@@ -100,6 +102,8 @@
   synthetic constructor •({required core::int numberOfLegs, required core::int numberOfWhiskers}) → self::Cat2
     : self::Cat2::numberOfLegs = numberOfLegs, self::Cat2::numberOfWhiskers = numberOfWhiskers, super core::Object::•()
     ;
+  operator /*isNullableByDefault*/ ==(core::Object other) → core::bool
+    return other is self::Cat2 && this.{self::Cat2::numberOfLegs}.{core::num::==}(other{self::Cat2}.{self::Cat2::numberOfLegs}) && this.{self::Cat2::numberOfWhiskers}.{core::num::==}(other{self::Cat2}.{self::Cat2::numberOfWhiskers});
 }
 static method main() → dynamic {
   self::Cat firstCat = invalid-expression "pkg/front_end/testcases/value_class/value_implements_non_value.dart:30:22: Error: No named parameter with the name 'numberOfLegs'.
diff --git a/pkg/front_end/testcases/value_class/value_implements_non_value.dart.weak.transformed.expect b/pkg/front_end/testcases/value_class/value_implements_non_value.dart.weak.transformed.expect
index 91e0d37..39979d6 100644
--- a/pkg/front_end/testcases/value_class/value_implements_non_value.dart.weak.transformed.expect
+++ b/pkg/front_end/testcases/value_class/value_implements_non_value.dart.weak.transformed.expect
@@ -87,6 +87,8 @@
   synthetic constructor •({required core::int numberOfLegs, required core::int numberOfWhiskers}) → self::Cat
     : self::Cat::numberOfLegs = numberOfLegs, self::Cat::numberOfWhiskers = numberOfWhiskers, super core::Object::•()
     ;
+  operator /*isNullableByDefault*/ ==(core::Object other) → core::bool
+    return other is self::Cat && this.{self::Cat::numberOfLegs}.{core::num::==}(other{self::Cat}.{self::Cat::numberOfLegs}) && this.{self::Cat::numberOfWhiskers}.{core::num::==}(other{self::Cat}.{self::Cat::numberOfWhiskers});
 }
 abstract class Animal2 extends core::Object {
   synthetic constructor •() → self::Animal2
@@ -100,6 +102,8 @@
   synthetic constructor •({required core::int numberOfLegs, required core::int numberOfWhiskers}) → self::Cat2
     : self::Cat2::numberOfLegs = numberOfLegs, self::Cat2::numberOfWhiskers = numberOfWhiskers, super core::Object::•()
     ;
+  operator /*isNullableByDefault*/ ==(core::Object other) → core::bool
+    return other is self::Cat2 && this.{self::Cat2::numberOfLegs}.{core::num::==}(other{self::Cat2}.{self::Cat2::numberOfLegs}) && this.{self::Cat2::numberOfWhiskers}.{core::num::==}(other{self::Cat2}.{self::Cat2::numberOfWhiskers});
 }
 static method main() → dynamic {
   self::Cat firstCat = invalid-expression "pkg/front_end/testcases/value_class/value_implements_non_value.dart:30:22: Error: No named parameter with the name 'numberOfLegs'.
diff --git a/pkg/front_end/testcases/value_class/value_mixin_error.dart.strong.expect b/pkg/front_end/testcases/value_class/value_mixin_error.dart.strong.expect
index a99cf80..18d691c 100644
--- a/pkg/front_end/testcases/value_class/value_mixin_error.dart.strong.expect
+++ b/pkg/front_end/testcases/value_class/value_mixin_error.dart.strong.expect
@@ -8,6 +8,8 @@
   synthetic constructor •() → self::A
     : super core::Object::•()
     ;
+  operator /*isNullableByDefault*/ ==(core::Object other) → core::bool
+    return other is self::A;
 }
 class B extends core::Object {
   synthetic constructor •() → self::B
diff --git a/pkg/front_end/testcases/value_class/value_mixin_error.dart.strong.transformed.expect b/pkg/front_end/testcases/value_class/value_mixin_error.dart.strong.transformed.expect
index 6c90e1e..338f201 100644
--- a/pkg/front_end/testcases/value_class/value_mixin_error.dart.strong.transformed.expect
+++ b/pkg/front_end/testcases/value_class/value_mixin_error.dart.strong.transformed.expect
@@ -8,6 +8,8 @@
   synthetic constructor •() → self::A
     : super core::Object::•()
     ;
+  operator /*isNullableByDefault*/ ==(core::Object other) → core::bool
+    return other is self::A;
 }
 class B extends core::Object {
   synthetic constructor •() → self::B
@@ -18,6 +20,8 @@
   synthetic constructor •() → self::_C&B&A
     : super self::B::•()
     ;
+  operator /*isNullableByDefault*/ ==(core::Object other) → core::bool
+    return other is self::A;
 }
 class C extends self::_C&B&A {
   synthetic constructor •() → self::C
diff --git a/pkg/front_end/testcases/value_class/value_mixin_error.dart.weak.expect b/pkg/front_end/testcases/value_class/value_mixin_error.dart.weak.expect
index a99cf80..18d691c 100644
--- a/pkg/front_end/testcases/value_class/value_mixin_error.dart.weak.expect
+++ b/pkg/front_end/testcases/value_class/value_mixin_error.dart.weak.expect
@@ -8,6 +8,8 @@
   synthetic constructor •() → self::A
     : super core::Object::•()
     ;
+  operator /*isNullableByDefault*/ ==(core::Object other) → core::bool
+    return other is self::A;
 }
 class B extends core::Object {
   synthetic constructor •() → self::B
diff --git a/pkg/front_end/testcases/value_class/value_mixin_error.dart.weak.transformed.expect b/pkg/front_end/testcases/value_class/value_mixin_error.dart.weak.transformed.expect
index 6c90e1e..338f201 100644
--- a/pkg/front_end/testcases/value_class/value_mixin_error.dart.weak.transformed.expect
+++ b/pkg/front_end/testcases/value_class/value_mixin_error.dart.weak.transformed.expect
@@ -8,6 +8,8 @@
   synthetic constructor •() → self::A
     : super core::Object::•()
     ;
+  operator /*isNullableByDefault*/ ==(core::Object other) → core::bool
+    return other is self::A;
 }
 class B extends core::Object {
   synthetic constructor •() → self::B
@@ -18,6 +20,8 @@
   synthetic constructor •() → self::_C&B&A
     : super self::B::•()
     ;
+  operator /*isNullableByDefault*/ ==(core::Object other) → core::bool
+    return other is self::A;
 }
 class C extends self::_C&B&A {
   synthetic constructor •() → self::C
diff --git a/pkg/kernel/lib/transformations/value_class.dart b/pkg/kernel/lib/transformations/value_class.dart
index 4b3acb0..afda4be 100644
--- a/pkg/kernel/lib/transformations/value_class.dart
+++ b/pkg/kernel/lib/transformations/value_class.dart
@@ -7,20 +7,23 @@
 import '../ast.dart';
 import '../kernel.dart';
 import '../core_types.dart' show CoreTypes;
+import '../class_hierarchy.dart' show ClassHierarchy;
 
-void transformComponent(Component node, CoreTypes coreTypes) {
+void transformComponent(
+    Component node, CoreTypes coreTypes, ClassHierarchy hierarchy) {
   for (Library library in node.libraries) {
     for (Class cls in library.classes) {
       if (isValueClass(cls)) {
-        transformValueClass(cls, coreTypes);
+        transformValueClass(cls, coreTypes, hierarchy);
       }
     }
   }
 }
 
-void transformValueClass(Class cls, CoreTypes coreTypes) {
+void transformValueClass(
+    Class cls, CoreTypes coreTypes, ClassHierarchy hierarchy) {
   addConstructor(cls, coreTypes);
-  // addEqualsOperator(cls, coreTypes);
+  addEqualsOperator(cls, coreTypes, hierarchy);
   // addHashCode(cls, coreTypes);
   // addCopyWith(cls);
 }
@@ -81,44 +84,77 @@
   }
   cls.annotations.removeAt(valueClassAnnotationIndex);
 }
-/*
 
-void addEqualsOperator(Class cls, CoreTypes coreTypes) {
-  Map<String, VariableDeclaration> environment = Map.fromIterable(cls.fields,
-      key: (f) => f.name.name,
-      value: (f) => VariableDeclaration(f.name.name, type: f.type));
-
-  VariableDeclaration other = VariableDeclaration("other");
-
-  var retType = cls.enclosingLibrary.isNonNullableByDefault
+void addEqualsOperator(
+    Class cls, CoreTypes coreTypes, ClassHierarchy hierarchy) {
+  for (Procedure procedure in cls.procedures) {
+    if (procedure.kind == ProcedureKind.Operator &&
+        procedure.name.name == "==") {
+      // ==operator is already implemented, spec is to do nothing
+      return;
+    }
+  }
+  DartType returnType = cls.enclosingLibrary.isNonNullableByDefault
       ? coreTypes.boolNonNullableRawType
       : coreTypes.boolLegacyRawType;
-  var myType = coreTypes.thisInterfaceType(cls, Nullability.nonNullable);
+  DartType myType = coreTypes.thisInterfaceType(cls, Nullability.nonNullable);
 
-  cls.addMember(Procedure(
+  Constructor superConstructor = null;
+  for (Constructor constructor in cls.superclass.constructors) {
+    if (constructor.name.name == "") {
+      superConstructor = constructor;
+    }
+  }
+  VariableDeclaration other = VariableDeclaration("other",
+      type: coreTypes.objectRawType(Nullability.nonNullable));
+  List<VariableDeclaration> allVariables = superConstructor
+      .function.namedParameters
+      .map<VariableDeclaration>(
+          (f) => VariableDeclaration(f.name, type: f.type))
+      .toList()
+        ..addAll(cls.fields.map<VariableDeclaration>(
+            (f) => VariableDeclaration(f.name.name, type: f.type)));
+
+  Map<VariableDeclaration, Member> targetsEquals = new Map();
+  Map<VariableDeclaration, Member> targets = new Map();
+  for (VariableDeclaration variable in allVariables) {
+    Member target = coreTypes.objectEquals;
+    Member targetEquals = coreTypes.objectEquals;
+    DartType fieldsType = variable.type;
+    if (fieldsType is InterfaceType) {
+      targetEquals =
+          hierarchy.getInterfaceMember(fieldsType.classNode, Name("=="));
+      target = hierarchy.getInterfaceMember(cls, Name(variable.name));
+    }
+    targetsEquals[variable] = targetEquals;
+    targets[variable] = target;
+  }
+
+  Procedure equalsOperator = Procedure(
       Name("=="),
       ProcedureKind.Operator,
       FunctionNode(
-          ReturnStatement(ConditionalExpression(
-              IsExpression(VariableGet(other), myType),
-              cls.fields
-                  .map((f) => MethodInvocation(
-                      PropertyGet(ThisExpression(), f.name, f),
-                      Name('=='),
-                      Arguments([
-                        PropertyGet(VariableGet(other, myType), f.name, f)
-                      ])))
-                  .toList()
-                  .fold(
-                      BoolLiteral(true),
-                      (previousValue, element) =>
-                          LogicalExpression(previousValue, '&&', element)),
-              BoolLiteral(false),
-              retType)),
-          returnType: retType,
-          positionalParameters: [other])));
+          ReturnStatement(allVariables
+              .map((f) => MethodInvocation(
+                  PropertyGet(ThisExpression(), Name(f.name), targets[f]),
+                  Name("=="),
+                  Arguments([
+                    PropertyGet(
+                        VariableGet(other, myType), Name(f.name), targets[f])
+                  ]),
+                  targetsEquals[f]))
+              .fold(
+                  IsExpression(VariableGet(other), myType),
+                  (previousValue, element) =>
+                      LogicalExpression(previousValue, '&&', element))),
+          returnType: returnType,
+          positionalParameters: [other]),
+      fileUri: cls.fileUri)
+    ..fileOffset = cls.fileOffset;
+  cls.addMember(equalsOperator);
 }
 
+/*
 void addHashCode(Class cls, CoreTypes coreTypes) {
   Map<String, VariableDeclaration> environment = Map.fromIterable(cls.fields,
       key: (f) => f.name.name,
@@ -126,7 +162,7 @@
 
   VariableDeclaration other = VariableDeclaration("other");
 
-  var retType = cls.enclosingLibrary.isNonNullableByDefault
+  var returnType = cls.enclosingLibrary.isNonNullableByDefault
       ? coreTypes.boolNonNullableRawType
       : coreTypes.boolLegacyRawType;
 
diff --git a/tools/VERSION b/tools/VERSION
index bf9bd94..67775da 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 10
 PATCH 0
-PRERELEASE 104
+PRERELEASE 105
 PRERELEASE_PATCH 0
\ No newline at end of file