[CFE] Coverage tool handles comments ignoring coverage

Allow comments to ignore some coverage block.
This is a step towards being able to (fake) push everything to 100%
covered, making a regression be nicely marked.

Also running `pkg/front_end/tool/coverage_merger.dart` with `--comment`
will add ignore comments.

Change-Id: I798dc2673eb71247af16356a7bd28d6629285a16
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/372501
Commit-Queue: Jens Johansen <jensj@google.com>
Reviewed-by: Johnni Winther <johnniwinther@google.com>
diff --git a/pkg/front_end/lib/src/kernel/constant_collection_builders.dart b/pkg/front_end/lib/src/kernel/constant_collection_builders.dart
index 1efe542..bef9a2e 100644
--- a/pkg/front_end/lib/src/kernel/constant_collection_builders.dart
+++ b/pkg/front_end/lib/src/kernel/constant_collection_builders.dart
@@ -105,7 +105,7 @@
     if (parts.last is List<Constant>) {
       lastPart = parts.last as List<Constant>;
     } else {
-      // Probably unreachable.
+      // Coverage-ignore: Probably unreachable.
       parts.add(lastPart = <Constant>[]);
     }
     Constant value = evaluator.ensureIsSubtype(constant, elementType, context);
@@ -177,7 +177,7 @@
     if (parts.last is List<Constant>) {
       lastPart = parts.last as List<Constant>;
     } else {
-      // Probably unreachable.
+      // Coverage-ignore: Probably unreachable.
       parts.add(lastPart = <Constant>[]);
     }
     Constant value = evaluator.ensureIsSubtype(constant, elementType, context);
@@ -291,7 +291,7 @@
     if (parts.last is List<ConstantMapEntry>) {
       lastPart = parts.last as List<ConstantMapEntry>;
     } else {
-      // Probably unreachable.
+      // Coverage-ignore: Probably unreachable.
       parts.add(lastPart = <ConstantMapEntry>[]);
     }
     if (!evaluator.hasPrimitiveEqual(key,
diff --git a/pkg/front_end/lib/src/kernel/constant_evaluator.dart b/pkg/front_end/lib/src/kernel/constant_evaluator.dart
index 1be0017..00176d6 100644
--- a/pkg/front_end/lib/src/kernel/constant_evaluator.dart
+++ b/pkg/front_end/lib/src/kernel/constant_evaluator.dart
@@ -86,6 +86,7 @@
       constantsTransformer.constantEvaluator.visitedLibraries);
 }
 
+// Coverage-ignore(suite): Only run from expression compilation.
 void transformProcedure(
     Procedure procedure,
     Target target,
@@ -208,6 +209,8 @@
     transformFieldList(library.fields, library);
 
     if (!keepFields) {
+      // Coverage-ignore: `keepFields` is currently always true. Maybe it should
+      // just be removed?
       // The transformer API does not iterate over `Library.additionalExports`,
       // so we manually delete the references to shaken nodes.
       library.additionalExports.removeWhere((Reference reference) {
@@ -218,6 +221,7 @@
     _exhaustivenessCache = null;
   }
 
+  // Coverage-ignore(suite): Only run from expression compilation.
   Procedure convertProcedure(Procedure node) {
     _exhaustivenessCache =
         new CfeExhaustivenessCache(constantEvaluator, node.enclosingLibrary);
@@ -2397,7 +2401,9 @@
           final List<LocatedMessage> contextMessages = <LocatedMessage>[
             locatedMessageActualError
           ];
-          if (result.context != null) contextMessages.addAll(result.context!);
+          if (result.context != null) {
+            contextMessages.addAll(result.context!);
+          }
           if (contextNode != null && contextNode != result.node) {
             contextMessages.add(
                 createLocatedMessage(contextNode, messageConstEvalContext));
@@ -2503,7 +2509,7 @@
       return status.error;
     } else if (status is ReturnStatus) {
       if (status.value == null) return null;
-      // Should not be reachable.
+      // Coverage-ignore: Should not be reachable.
       return createEvaluationErrorConstant(
           constructor,
           templateConstEvalError
@@ -3002,7 +3008,7 @@
 
     final Class klass = constructor.enclosingClass;
     if (klass.isAbstract) {
-      // Probably unreachable.
+      // Coverage-ignore: Probably unreachable.
       return createExpressionErrorConstant(
           node, templateAbstractClassInstantiation.withArguments(klass.name));
     }
@@ -3060,7 +3066,7 @@
 
     // Fill in any missing type arguments with "dynamic".
     for (int i = typeArguments.length; i < klass.typeParameters.length; i++) {
-      // Probably unreachable.
+      // Coverage-ignore: Probably unreachable.
       typeArguments.add(const DynamicType());
     }
 
@@ -3096,7 +3102,7 @@
     if (constructor.function.body != null &&
         constructor.function.body is! EmptyStatement &&
         !enableConstFunctions) {
-      // Probably unreachable.
+      // Coverage-ignore: Probably unreachable.
       return createExpressionErrorConstant(
           node, messageConstConstructorWithBody);
     } else if (constructor.isExternal) {
@@ -3268,8 +3274,9 @@
           AbortConstant? error = checkAssert(init.statement);
           if (error != null) return error;
         } else {
+          // Coverage-ignore: Probably unreachable.
           // InvalidInitializer or new Initializers.
-          // Probably unreachable. InvalidInitializer is (currently) only
+          // InvalidInitializer is (currently) only
           // created for classes with no constructors that doesn't have a
           // super that takes no arguments. It thus cannot be const.
           // Explicit constructors with incorrect super calls will get a
@@ -3851,7 +3858,6 @@
           if (right is BoolConstant || right is UnevaluatedConstant) {
             return right;
           }
-
           return createEvaluationErrorConstant(
               node,
               templateConstEvalInvalidBinaryOperandType.withArguments(
@@ -3873,7 +3879,6 @@
           if (right is BoolConstant || right is UnevaluatedConstant) {
             return right;
           }
-
           return createEvaluationErrorConstant(
               node,
               templateConstEvalInvalidBinaryOperandType.withArguments(
@@ -3921,8 +3926,8 @@
   @override
   Constant visitInstanceGet(InstanceGet node) {
     if (node.receiver is ThisExpression) {
-      // Probably unreachable unless trying to evaluate non-const stuff as
-      // const.
+      // Coverage-ignore: Probably unreachable unless trying to evaluate
+      // non-const stuff as const.
       // Access "this" during instance creation.
       if (instanceBuilder == null) {
         return createEvaluationErrorConstant(
@@ -3939,7 +3944,7 @@
 
       // Meant as a "stable backstop for situations where Fasta fails to
       // rewrite various erroneous constructs into invalid expressions".
-      // Probably unreachable.
+      // Coverage-ignore: Probably unreachable.
       return createEvaluationErrorConstant(
           node,
           templateConstEvalError.withArguments(
@@ -4249,7 +4254,7 @@
         } else if (defaultValue is NullConstant) {
           boolConstant = nullConstant;
         } else {
-          // Probably unreachable.
+          // Coverage-ignore: Probably unreachable.
           boolConstant = falseConstant;
         }
       } else {
@@ -4287,8 +4292,8 @@
       }
       return stringConstant;
     }
-    // Unreachable until fromEnvironment is added to other classes in dart:core
-    // than bool, int and String.
+    // Coverage-ignore: Unreachable until fromEnvironment is added to other
+    // classes in dart:core than bool, int and String.
     throw new UnsupportedError(
         'Unexpected fromEnvironment constructor: $target');
   }
@@ -4639,7 +4644,7 @@
         return canonicalize(
             new InstantiationConstant(constant, convertTypes(types)));
       } else {
-        // Probably unreachable.
+        // Coverage-ignore: Probably unreachable.
         return createEvaluationErrorConstant(
             node,
             templateConstEvalError.withArguments(
@@ -4650,7 +4655,7 @@
     }
     // The inner expression in an instantiation can never be null, since
     // instantiations are only inferred on direct references to declarations.
-    // Probably unreachable.
+    // Coverage-ignore: Probably unreachable.
     return createEvaluationErrorConstant(
         node,
         templateConstEvalError.withArguments(
@@ -4682,7 +4687,7 @@
       return canonicalize(
           new TypedefTearOffConstant(typeParameters, constant, typeArguments));
     } else {
-      // Probably unreachable.
+      // Coverage-ignore: Probably unreachable.
       return createEvaluationErrorConstant(
           node,
           templateConstEvalError.withArguments(
@@ -4827,7 +4832,7 @@
     if (targetingJavaScript && !result) {
       if (constantType is InterfaceType &&
           constantType.classNode == typeEnvironment.coreTypes.intClass) {
-        // Probably unreachable.
+        // Coverage-ignore: Probably unreachable.
         // With JS semantics, an integer is also a double.
         result = typeEnvironment.isSubtypeOf(
             new InterfaceType(typeEnvironment.coreTypes.doubleClass,
@@ -5059,7 +5064,7 @@
         return makeBoolConstant(a > b);
     }
 
-    // Probably unreachable.
+    // Coverage-ignore: Probably unreachable.
     return createExpressionErrorConstant(node,
         templateNotConstantExpression.withArguments("Binary '$op' operation"));
   }
@@ -5214,7 +5219,9 @@
   @override
   ExecutionStatus visitIfStatement(IfStatement node) {
     Constant condition = evaluate(node.condition);
-    if (condition is AbortConstant) return new AbortStatus(condition);
+    if (condition is AbortConstant) {
+      return new AbortStatus(condition);
+    }
     assert(condition is BoolConstant);
     if ((condition as BoolConstant).value) {
       return node.then.accept(this);
@@ -5251,7 +5258,9 @@
       }
     }
 
-    if (condition is AbortConstant) return new AbortStatus(condition);
+    if (condition is AbortConstant) {
+      return new AbortStatus(condition);
+    }
     assert(condition is BoolConstant);
     return const ProceedStatus();
   }
@@ -5285,7 +5294,9 @@
   @override
   ExecutionStatus visitSwitchStatement(SwitchStatement node) {
     final Constant value = evaluate(node.expression);
-    if (value is AbortConstant) return new AbortStatus(value);
+    if (value is AbortConstant) {
+      return new AbortStatus(value);
+    }
 
     for (SwitchCase switchCase in node.cases) {
       if (switchCase.isDefault) return switchCase.body.accept(this);
@@ -5376,7 +5387,9 @@
       if (status is! ProceedStatus) return status;
       condition = evaluate(node.condition);
     }
-    if (condition is AbortConstant) return new AbortStatus(condition);
+    if (condition is AbortConstant) {
+      return new AbortStatus(condition);
+    }
     assert(condition is BoolConstant);
     return const ProceedStatus();
   }
diff --git a/pkg/front_end/lib/src/kernel/constant_int_folder.dart b/pkg/front_end/lib/src/kernel/constant_int_folder.dart
index ee1fa41..43fc143 100644
--- a/pkg/front_end/lib/src/kernel/constant_int_folder.dart
+++ b/pkg/front_end/lib/src/kernel/constant_int_folder.dart
@@ -85,7 +85,7 @@
       case '~':
         return new IntConstant(~operand.value);
       default:
-        // Probably unreachable.
+        // Coverage-ignore: Probably unreachable.
         return evaluator.createExpressionErrorConstant(
             node,
             templateNotConstantExpression
@@ -136,7 +136,7 @@
       case '>':
         return evaluator.makeBoolConstant(a > b);
       default:
-        // Probably unreachable.
+        // Coverage-ignore: Probably unreachable.
         return evaluator.createExpressionErrorConstant(
             node,
             templateNotConstantExpression
@@ -202,7 +202,7 @@
         int intValue = _toUint32(operand.value);
         return new DoubleConstant(_truncate32(~intValue).toDouble());
       default:
-        // Probably unreachable.
+        // Coverage-ignore: Probably unreachable.
         return evaluator.createExpressionErrorConstant(
             node,
             templateNotConstantExpression
@@ -259,7 +259,7 @@
       case '>':
         return evaluator.makeBoolConstant(a > b);
       default:
-        // Probably unreachable.
+        // Coverage-ignore: Probably unreachable.
         return evaluator.createExpressionErrorConstant(
             node,
             templateNotConstantExpression
diff --git a/pkg/front_end/test/coverage_suite.dart b/pkg/front_end/test/coverage_suite.dart
index d0413cb..50dc957 100644
--- a/pkg/front_end/test/coverage_suite.dart
+++ b/pkg/front_end/test/coverage_suite.dart
@@ -68,6 +68,8 @@
     Uri.base.resolve(".dart_tool/package_config.json"),
     coverageTmpDir.uri,
     silent: true,
+    extraCoverageIgnores: ["coverage-ignore(suite):"],
+    extraCoverageBlockIgnores: ["coverage-ignore-block(suite):"],
   );
   if (coverageData == null) throw "Failure in coverage.";
 
@@ -126,6 +128,7 @@
       int hitCount = coverageEntry.value.hitCount;
       int missCount = coverageEntry.value.missCount;
       double percent = (hitCount / (hitCount + missCount) * 100);
+      if (percent.isNaN) percent = 100;
       if (options.updateExpectations) {
         updatedExpectations.writeln("  // $percent%.");
         updatedExpectations.writeln("  \"${coverageEntry.key}\": "
@@ -142,6 +145,7 @@
         double expectedPercent = (expected.hitCount /
             (expected.hitCount + expected.missCount) *
             100);
+        if (expectedPercent.isNaN) expectedPercent = 100;
         int requireAtLeast = expectedPercent.floor();
         pass = percent >= requireAtLeast;
         if (!pass) {
diff --git a/pkg/front_end/test/coverage_suite_expected.dart b/pkg/front_end/test/coverage_suite_expected.dart
index d41bbc8..b1f2449 100644
--- a/pkg/front_end/test/coverage_suite_expected.dart
+++ b/pkg/front_end/test/coverage_suite_expected.dart
@@ -8,25 +8,20 @@
 // using out/ReleaseX64/dart-sdk/bin/dart (which for instance makes a
 // difference for compute_platform_binaries_location.dart).
 const Map<String, ({int hitCount, int missCount})> _expect = {
-  // 18.53448275862069%.
+  // 18.614718614718615%.
   "package:front_end/src/api_prototype/compiler_options.dart": (
     hitCount: 43,
-    missCount: 189,
+    missCount: 188,
   ),
   // 89.1891891891892%.
   "package:front_end/src/api_prototype/experimental_flags.dart": (
     hitCount: 66,
     missCount: 8,
   ),
-  // 54.07925407925408%.
-  "package:front_end/src/api_prototype/experimental_flags_generated.dart": (
-    hitCount: 232,
-    missCount: 197,
-  ),
-  // 33.33333333333333%.
+  // 100.0%.
   "package:front_end/src/api_prototype/file_system.dart": (
     hitCount: 2,
-    missCount: 4,
+    missCount: 0,
   ),
   // 6.666666666666667%.
   "package:front_end/src/api_prototype/incremental_kernel_generator.dart": (
@@ -41,22 +36,22 @@
   // 0.0%.
   "package:front_end/src/api_prototype/language_version.dart": (
     hitCount: 0,
-    missCount: 67,
+    missCount: 65,
   ),
-  // 3.903903903903904%.
+  // 3.927492447129909%.
   "package:front_end/src/api_prototype/lowering_predicates.dart": (
     hitCount: 13,
-    missCount: 320,
+    missCount: 318,
   ),
-  // 25.0%.
+  // 27.710843373493976%.
   "package:front_end/src/api_prototype/memory_file_system.dart": (
     hitCount: 23,
-    missCount: 69,
+    missCount: 60,
   ),
-  // 38.46153846153847%.
+  // 38.83495145631068%.
   "package:front_end/src/api_prototype/standard_file_system.dart": (
     hitCount: 40,
-    missCount: 64,
+    missCount: 63,
   ),
   // 0.0%.
   "package:front_end/src/api_prototype/summary_generator.dart": (
@@ -98,10 +93,10 @@
     hitCount: 70,
     missCount: 29,
   ),
-  // 90.1639344262295%.
+  // 93.22033898305084%.
   "package:front_end/src/base/compiler_context.dart": (
     hitCount: 55,
-    missCount: 6,
+    missCount: 4,
   ),
   // 100.0%.
   "package:front_end/src/base/configuration.dart": (
@@ -143,10 +138,10 @@
     hitCount: 96,
     missCount: 3,
   ),
-  // 50.55718475073314%.
+  // 50.67607289829512%.
   "package:front_end/src/base/incremental_compiler.dart": (
     hitCount: 862,
-    missCount: 843,
+    missCount: 839,
   ),
   // 0.0%.
   "package:front_end/src/base/incremental_serializer.dart": (
@@ -183,15 +178,15 @@
     hitCount: 0,
     missCount: 29,
   ),
-  // 39.86486486486486%.
+  // 40.75993091537133%.
   "package:front_end/src/base/processed_options.dart": (
     hitCount: 236,
-    missCount: 356,
+    missCount: 343,
   ),
-  // 80.62563067608475%.
+  // 84.01682439537329%.
   "package:front_end/src/base/scope.dart": (
     hitCount: 799,
-    missCount: 192,
+    missCount: 152,
   ),
   // 73.07692307692307%.
   "package:front_end/src/base/ticker.dart": (
@@ -218,10 +213,10 @@
     hitCount: 0,
     missCount: 17,
   ),
-  // 64.44444444444444%.
+  // 72.5%.
   "package:front_end/src/builder/builder.dart": (
     hitCount: 29,
-    missCount: 16,
+    missCount: 11,
   ),
   // 100.0%.
   "package:front_end/src/builder/builder_mixins.dart": (
@@ -253,10 +248,10 @@
     hitCount: 2,
     missCount: 0,
   ),
-  // 50.0%.
+  // 100.0%.
   "package:front_end/src/builder/extension_builder.dart": (
     hitCount: 4,
-    missCount: 4,
+    missCount: 0,
   ),
   // 100.0%.
   "package:front_end/src/builder/extension_type_declaration_builder.dart": (
@@ -343,15 +338,15 @@
     hitCount: 24,
     missCount: 0,
   ),
-  // 34.146341463414636%.
+  // 37.83783783783784%.
   "package:front_end/src/builder/omitted_type_builder.dart": (
     hitCount: 28,
-    missCount: 54,
+    missCount: 46,
   ),
   // 0.0%.
   "package:front_end/src/builder/omitted_type_declaration_builder.dart": (
     hitCount: 0,
-    missCount: 13,
+    missCount: 5,
   ),
   // 89.74358974358975%.
   "package:front_end/src/builder/prefix_builder.dart": (
@@ -368,10 +363,10 @@
     hitCount: 186,
     missCount: 50,
   ),
-  // 77.77777777777779%.
+  // 82.35294117647058%.
   "package:front_end/src/builder/type_builder.dart": (
     hitCount: 56,
-    missCount: 16,
+    missCount: 12,
   ),
   // 90.0%.
   "package:front_end/src/builder/type_declaration_builder.dart": (
@@ -388,15 +383,10 @@
     hitCount: 2,
     missCount: 0,
   ),
-  // 73.0892742453436%.
-  "package:front_end/src/codes/cfe_codes_generated.dart": (
-    hitCount: 1138,
-    missCount: 419,
-  ),
-  // 83.68336025848141%.
+  // 85.0574712643678%.
   "package:front_end/src/codes/type_labeler.dart": (
     hitCount: 518,
-    missCount: 101,
+    missCount: 91,
   ),
   // 71.23287671232876%.
   "package:front_end/src/compute_platform_binaries_location.dart": (
@@ -408,10 +398,10 @@
     hitCount: 16,
     missCount: 0,
   ),
-  // 92.34972677595628%.
+  // 93.37016574585635%.
   "package:front_end/src/dill/dill_class_builder.dart": (
     hitCount: 169,
-    missCount: 14,
+    missCount: 12,
   ),
   // 86.74698795180723%.
   "package:front_end/src/dill/dill_extension_builder.dart": (
@@ -433,10 +423,10 @@
     hitCount: 129,
     missCount: 25,
   ),
-  // 83.12342569269522%.
+  // 84.61538461538461%.
   "package:front_end/src/dill/dill_library_builder.dart": (
     hitCount: 330,
-    missCount: 67,
+    missCount: 60,
   ),
   // 77.03349282296651%.
   "package:front_end/src/dill/dill_loader.dart": (
@@ -468,70 +458,70 @@
     hitCount: 0,
     missCount: 128,
   ),
-  // 92.09016940109377%.
+  // 92.1639300493926%.
   "package:front_end/src/kernel/body_builder.dart": (
     hitCount: 6904,
-    missCount: 593,
+    missCount: 587,
   ),
-  // 70.40816326530613%.
+  // 91.26984126984127%.
   "package:front_end/src/kernel/body_builder_context.dart": (
     hitCount: 345,
-    missCount: 145,
+    missCount: 33,
   ),
-  // 36.44736842105264%.
+  // 36.83510638297872%.
   "package:front_end/src/kernel/collections.dart": (
     hitCount: 277,
-    missCount: 483,
+    missCount: 475,
   ),
-  // 91.8854415274463%.
+  // 92.54807692307693%.
   "package:front_end/src/kernel/combined_member_signature.dart": (
     hitCount: 385,
-    missCount: 34,
+    missCount: 31,
   ),
   // 61.68831168831169%.
   "package:front_end/src/kernel/const_conditional_simplifier.dart": (
     hitCount: 95,
     missCount: 59,
   ),
-  // 67.65676567656766%.
+  // 69.72789115646259%.
   "package:front_end/src/kernel/constant_collection_builders.dart": (
     hitCount: 205,
-    missCount: 98,
+    missCount: 89,
   ),
-  // 82.66337300696159%.
+  // 85.85694379934975%.
   "package:front_end/src/kernel/constant_evaluator.dart": (
-    hitCount: 3681,
-    missCount: 772,
+    hitCount: 3697,
+    missCount: 609,
   ),
-  // 92.04545454545455%.
+  // 97.59036144578313%.
   "package:front_end/src/kernel/constant_int_folder.dart": (
     hitCount: 243,
-    missCount: 21,
+    missCount: 6,
   ),
   // 95.11278195488721%.
   "package:front_end/src/kernel/constructor_tearoff_lowering.dart": (
     hitCount: 253,
     missCount: 13,
   ),
-  // 74.57098283931357%.
+  // 75.0392464678179%.
   "package:front_end/src/kernel/exhaustiveness.dart": (
     hitCount: 478,
-    missCount: 163,
+    missCount: 159,
   ),
-  // 79.7660448940879%.
+  // 79.81651376146789%.
   "package:front_end/src/kernel/expression_generator.dart": (
     hitCount: 2523,
-    missCount: 640,
+    missCount: 638,
   ),
   // 100.0%.
   "package:front_end/src/kernel/expression_generator_helper.dart": (
     hitCount: 36,
     missCount: 0,
   ),
-  // 90.27777777777779%.
+  // 93.97590361445783%.
   "package:front_end/src/kernel/forest.dart": (
     hitCount: 390,
-    missCount: 42,
+    missCount: 25,
   ),
   // 94.4927536231884%.
   "package:front_end/src/kernel/forwarding_node.dart": (
@@ -548,10 +538,10 @@
     hitCount: 218,
     missCount: 0,
   ),
-  // 87.52834467120182%.
+  // 87.92710706150342%.
   "package:front_end/src/kernel/hierarchy/extension_type_members.dart": (
     hitCount: 386,
-    missCount: 55,
+    missCount: 53,
   ),
   // 50.77720207253886%.
   "package:front_end/src/kernel/hierarchy/hierarchy_builder.dart": (
@@ -578,25 +568,25 @@
     hitCount: 248,
     missCount: 155,
   ),
-  // 52.24719101123596%.
+  // 60.3896103896104%.
   "package:front_end/src/kernel/implicit_field_type.dart": (
     hitCount: 93,
-    missCount: 85,
+    missCount: 61,
   ),
-  // 2.941176470588235%.
+  // 5.555555555555555%.
   "package:front_end/src/kernel/implicit_type_argument.dart": (
     hitCount: 1,
-    missCount: 33,
+    missCount: 17,
   ),
-  // 46.484375%.
+  // 47.25972994440032%.
   "package:front_end/src/kernel/internal_ast.dart": (
     hitCount: 595,
-    missCount: 685,
+    missCount: 664,
   ),
-  // 74.13793103448276%.
+  // 78.18181818181819%.
   "package:front_end/src/kernel/invalid_type.dart": (
     hitCount: 43,
-    missCount: 15,
+    missCount: 12,
   ),
   // 55.55555555555556%.
   "package:front_end/src/kernel/kernel_constants.dart": (
@@ -608,10 +598,10 @@
     hitCount: 285,
     missCount: 3,
   ),
-  // 81.10175975516451%.
+  // 81.35072908672295%.
   "package:front_end/src/kernel/kernel_target.dart": (
     hitCount: 1060,
-    missCount: 247,
+    missCount: 243,
   ),
   // 61.111111111111114%.
   "package:front_end/src/kernel/kernel_variable_builder.dart": (
@@ -636,17 +626,17 @@
   // 0.0%.
   "package:front_end/src/kernel/macro/identifiers.dart": (
     hitCount: 0,
-    missCount: 132,
+    missCount: 120,
   ),
   // 0.0%.
   "package:front_end/src/kernel/macro/introspectors.dart": (
     hitCount: 0,
-    missCount: 573,
+    missCount: 549,
   ),
-  // 0.19047619047619047%.
+  // 0.1932367149758454%.
   "package:front_end/src/kernel/macro/macro.dart": (
     hitCount: 2,
-    missCount: 1048,
+    missCount: 1033,
   ),
   // 0.0%.
   "package:front_end/src/kernel/macro/offsets.dart": (
@@ -658,10 +648,10 @@
     hitCount: 0,
     missCount: 230,
   ),
-  // 89.23611111111111%.
+  // 91.13475177304964%.
   "package:front_end/src/kernel/member_covariance.dart": (
     hitCount: 257,
-    missCount: 31,
+    missCount: 25,
   ),
   // 39.473684210526315%.
   "package:front_end/src/kernel/resource_identifier.dart": (
@@ -673,20 +663,20 @@
     hitCount: 16,
     missCount: 89,
   ),
-  // 19.753086419753085%.
+  // 20.77922077922078%.
   "package:front_end/src/kernel/try_constant_evaluator.dart": (
     hitCount: 16,
-    missCount: 65,
+    missCount: 61,
   ),
   // 94.27402862985686%.
   "package:front_end/src/kernel/type_algorithms.dart": (
     hitCount: 922,
     missCount: 56,
   ),
-  // 90.20618556701031%.
+  // 92.10526315789474%.
   "package:front_end/src/kernel/type_builder_computer.dart": (
     hitCount: 175,
-    missCount: 19,
+    missCount: 15,
   ),
   // 37.93103448275862%.
   "package:front_end/src/kernel/utils.dart": (
@@ -698,10 +688,10 @@
     hitCount: 18,
     missCount: 14,
   ),
-  // 28.79581151832461%.
+  // 28.947368421052634%.
   "package:front_end/src/kernel_generator_impl.dart": (
     hitCount: 55,
-    missCount: 136,
+    missCount: 135,
   ),
   // 0.0%.
   "package:front_end/src/macros/isolate_macro_serializer.dart": (
@@ -733,20 +723,20 @@
     hitCount: 163,
     missCount: 40,
   ),
-  // 92.1875%.
+  // 93.38129496402877%.
   "package:front_end/src/source/diet_listener.dart": (
     hitCount: 649,
-    missCount: 55,
+    missCount: 46,
   ),
   // 100.0%.
   "package:front_end/src/source/diet_parser.dart": (
     hitCount: 4,
     missCount: 0,
   ),
-  // 93.19148936170212%.
+  // 93.99141630901288%.
   "package:front_end/src/source/name_scheme.dart": (
     hitCount: 219,
-    missCount: 16,
+    missCount: 14,
   ),
   // 95.16129032258065%.
   "package:front_end/src/source/offset_map.dart": (
@@ -773,20 +763,20 @@
     hitCount: 1213,
     missCount: 197,
   ),
-  // 92.65658747300216%.
+  // 93.058568329718%.
   "package:front_end/src/source/source_constructor_builder.dart": (
     hitCount: 858,
-    missCount: 68,
+    missCount: 64,
   ),
   // 95.73560767590618%.
   "package:front_end/src/source/source_enum_builder.dart": (
     hitCount: 449,
     missCount: 20,
   ),
-  // 61.261261261261254%.
+  // 64.15094339622641%.
   "package:front_end/src/source/source_extension_builder.dart": (
     hitCount: 68,
-    missCount: 43,
+    missCount: 38,
   ),
   // 84.32539682539682%.
   "package:front_end/src/source/source_extension_type_declaration_builder.dart":
@@ -799,30 +789,30 @@
     hitCount: 581,
     missCount: 49,
   ),
-  // 89.94668697638994%.
+  // 94.2537909018356%.
   "package:front_end/src/source/source_field_builder.dart": (
     hitCount: 1181,
-    missCount: 132,
+    missCount: 72,
   ),
   // 89.39393939393939%.
   "package:front_end/src/source/source_function_builder.dart": (
     hitCount: 295,
     missCount: 35,
   ),
-  // 85.35513790578472%.
+  // 85.2467024914509%.
   "package:front_end/src/source/source_library_builder.dart": (
-    hitCount: 3497,
-    missCount: 600,
+    hitCount: 3490,
+    missCount: 604,
   ),
   // 81.8988464951198%.
   "package:front_end/src/source/source_loader.dart": (
     hitCount: 1846,
     missCount: 408,
   ),
-  // 40.32258064516129%.
+  // 50.0%.
   "package:front_end/src/source/source_member_builder.dart": (
     hitCount: 25,
-    missCount: 37,
+    missCount: 25,
   ),
   // 96.11829944547135%.
   "package:front_end/src/source/source_procedure_builder.dart": (
@@ -864,15 +854,15 @@
     hitCount: 120,
     missCount: 39,
   ),
-  // 85.12820512820512%.
+  // 87.36842105263159%.
   "package:front_end/src/type_inference/inference_results.dart": (
     hitCount: 166,
-    missCount: 29,
+    missCount: 24,
   ),
-  // 90.37792719684674%.
+  // 90.44083526682135%.
   "package:front_end/src/type_inference/inference_visitor.dart": (
     hitCount: 7796,
-    missCount: 830,
+    missCount: 824,
   ),
   // 85.96491228070175%.
   "package:front_end/src/type_inference/inference_visitor_base.dart": (
@@ -889,10 +879,10 @@
     hitCount: 519,
     missCount: 10,
   ),
-  // 77.63578274760383%.
+  // 81.13522537562604%.
   "package:front_end/src/type_inference/object_access_target.dart": (
     hitCount: 486,
-    missCount: 140,
+    missCount: 113,
   ),
   // 98.0%.
   "package:front_end/src/type_inference/shared_type_analyzer.dart": (
@@ -914,20 +904,20 @@
     hitCount: 19,
     missCount: 1,
   ),
-  // 89.62962962962962%.
+  // 90.29850746268657%.
   "package:front_end/src/type_inference/type_inference_engine.dart": (
     hitCount: 484,
-    missCount: 56,
+    missCount: 52,
   ),
   // 54.037267080745345%.
   "package:front_end/src/type_inference/type_inferrer.dart": (
     hitCount: 87,
     missCount: 74,
   ),
-  // 36.666666666666664%.
+  // 42.30769230769231%.
   "package:front_end/src/type_inference/type_schema.dart": (
     hitCount: 11,
-    missCount: 19,
+    missCount: 15,
   ),
   // 88.88888888888889%.
   "package:front_end/src/type_inference/type_schema_elimination.dart": (
@@ -954,15 +944,10 @@
     hitCount: 20,
     missCount: 18,
   ),
-  // 5.5954088952654235%.
+  // 5.611510791366906%.
   "package:front_end/src/util/parser_ast.dart": (
     hitCount: 78,
-    missCount: 1316,
-  ),
-  // 20.385501780850618%.
-  "package:front_end/src/util/parser_ast_helper.dart": (
-    hitCount: 973,
-    missCount: 3800,
+    missCount: 1312,
   ),
   // 86.54205607476636%.
   "package:front_end/src/util/textual_outline.dart": (
diff --git a/pkg/front_end/test/run_all_coverage.dart b/pkg/front_end/test/run_all_coverage.dart
index e955659..5dea1b2 100644
--- a/pkg/front_end/test/run_all_coverage.dart
+++ b/pkg/front_end/test/run_all_coverage.dart
@@ -116,5 +116,7 @@
     repoDirUri.resolve(".dart_tool/package_config.json"),
     coverageTmpDir.uri,
     silent: false,
+    extraCoverageIgnores: const [],
+    extraCoverageBlockIgnores: const [],
   );
 }
diff --git a/pkg/front_end/test/spell_checking_list_tests.txt b/pkg/front_end/test/spell_checking_list_tests.txt
index 99c03ad..5d8edc6 100644
--- a/pkg/front_end/test/spell_checking_list_tests.txt
+++ b/pkg/front_end/test/spell_checking_list_tests.txt
@@ -50,6 +50,7 @@
 area
 arglebargle
 arr
+arrive
 asdf
 asserter
 assure
@@ -613,6 +614,7 @@
 prerequisite
 press
 pretends
+prettier
 preventing
 primitives
 printouts
@@ -668,6 +670,7 @@
 reorder
 reordering
 repaint
+replaceable
 representative
 repro
 reproduce
@@ -791,6 +794,7 @@
 te
 templates
 theoretically
+there'll
 thereby
 thereof
 thread
@@ -834,6 +838,7 @@
 uncompiled
 unconverted
 uncover
+uncovered
 uncovers
 underline
 undocumented
diff --git a/pkg/front_end/testcases/general/constants/various.dart b/pkg/front_end/testcases/general/constants/various.dart
index bd7c03e..43f79de 100644
--- a/pkg/front_end/testcases/general/constants/various.dart
+++ b/pkg/front_end/testcases/general/constants/various.dart
@@ -111,6 +111,16 @@
         this.y = "hello".length;
 }
 
+class FooWithHashCodeField {
+  final int x;
+  final int y;
+  final int hashCode;
+  const FooWithHashCodeField(int x)
+      : this.x = x,
+        this.y = "hello".length,
+        this.hashCode = x * 42;
+}
+
 class ExtendsFoo1 extends Foo {
   // No constructor.
 }
@@ -127,6 +137,12 @@
 const Foo foo2 = const Foo(42);
 const bool foosIdentical = identical(foo1, foo2);
 const bool foosEqual = foo1 == foo2;
+
+const FooWithHashCodeField fooWithHashCodeField1 = const FooWithHashCodeField(42);
+const FooWithHashCodeField fooWithHashCodeField2 = const FooWithHashCodeField(42);
+const bool fooWithHashCodeFieldIdentical = identical(fooWithHashCodeField1, fooWithHashCodeField2);
+const bool fooWithHashCodeFieldEqual = fooWithHashCodeField1 == fooWithHashCodeField2;
+
 const Symbol barFoo = const Symbol("Foo");
 const Symbol barFooEqual = const Symbol("Foo=");
 const Symbol tripleShiftSymbol = const Symbol(">>>");
diff --git a/pkg/front_end/testcases/general/constants/various.dart.strong.expect b/pkg/front_end/testcases/general/constants/various.dart.strong.expect
index 8e88044..01d5b41 100644
--- a/pkg/front_end/testcases/general/constants/various.dart.strong.expect
+++ b/pkg/front_end/testcases/general/constants/various.dart.strong.expect
@@ -2,7 +2,7 @@
 //
 // Problems in library:
 //
-// pkg/front_end/testcases/general/constants/various.dart:162:3: Error: A const constructor can't have a body.
+// pkg/front_end/testcases/general/constants/various.dart:178:3: Error: A const constructor can't have a body.
 // Try removing either the 'const' keyword or the body.
 //   const ClassWithNonEmptyConstConstructor() {
 //   ^^^^^
@@ -39,11 +39,11 @@
 // const y4 = y++;
 //            ^
 //
-// pkg/front_end/testcases/general/constants/various.dart:140:24: Error: Not a constant expression.
+// pkg/front_end/testcases/general/constants/various.dart:156:24: Error: Not a constant expression.
 // const function_const = () {};
 //                        ^^
 //
-// pkg/front_end/testcases/general/constants/various.dart:180:14: Error: Can't access 'this' in a field initializer to read 'y'.
+// pkg/front_end/testcases/general/constants/various.dart:196:14: Error: Can't access 'this' in a field initializer to read 'y'.
 //   final z1 = y;
 //              ^
 //
@@ -71,20 +71,20 @@
 //   @AbstractClassWithConstructor()
 //    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 //
-// pkg/front_end/testcases/general/constants/various.dart:118:39: Error: Cannot invoke a non-'const' constructor where a const expression is expected.
+// pkg/front_end/testcases/general/constants/various.dart:128:39: Error: Cannot invoke a non-'const' constructor where a const expression is expected.
 // Try using a constructor or factory that is 'const'.
 // const ExtendsFoo1 extendsFoo1 = const ExtendsFoo1();
 //                                       ^^^^^^^^^^^
 //
-// pkg/front_end/testcases/general/constants/various.dart:121:9: Error: The superclass, 'Foo', has no unnamed constructor that takes no arguments.
+// pkg/front_end/testcases/general/constants/various.dart:131:9: Error: The superclass, 'Foo', has no unnamed constructor that takes no arguments.
 //   const ExtendsFoo2();
 //         ^^^^^^^^^^^
 //
-// pkg/front_end/testcases/general/constants/various.dart:180:14: Error: Not a constant expression.
+// pkg/front_end/testcases/general/constants/various.dart:196:14: Error: Not a constant expression.
 //   final z1 = y;
 //              ^
 //
-// pkg/front_end/testcases/general/constants/various.dart:181:14: Error: Not a constant expression.
+// pkg/front_end/testcases/general/constants/various.dart:197:14: Error: Not a constant expression.
 //   final z2 = x;
 //              ^
 //
@@ -92,7 +92,7 @@
 // const AbstractClassWithConstructor();
 //       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 //
-// pkg/front_end/testcases/general/constants/various.dart:168:7: Error: Cannot invoke a non-'const' constructor where a const expression is expected.
+// pkg/front_end/testcases/general/constants/various.dart:184:7: Error: Cannot invoke a non-'const' constructor where a const expression is expected.
 // Try using a constructor or factory that is 'const'.
 // const ClassWithNonEmptyConstConstructor();
 //       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -107,7 +107,7 @@
 //   Object bar;
 //          ^^^
 //
-// pkg/front_end/testcases/general/constants/various.dart:114:7: Error: The superclass, 'Foo', has no unnamed constructor that takes no arguments.
+// pkg/front_end/testcases/general/constants/various.dart:124:7: Error: The superclass, 'Foo', has no unnamed constructor that takes no arguments.
 // class ExtendsFoo1 extends Foo {
 //       ^
 //
@@ -181,37 +181,48 @@
 // const x4 = x++;
 //       ^
 //
-// pkg/front_end/testcases/general/constants/various.dart:135:26: Error: Constant evaluation error:
+// pkg/front_end/testcases/general/constants/various.dart:144:62: Error: Constant evaluation error:
+// const bool fooWithHashCodeFieldEqual = fooWithHashCodeField1 == fooWithHashCodeField2;
+//                                                              ^
+// pkg/front_end/testcases/general/constants/various.dart:144:62: Context: Binary operator '==' requires receiver constant 'FooWithHashCodeField {x: 42, y: 5, hashCode: 1764}' of a type with primitive equality or type 'double', but was of type 'FooWithHashCodeField'.
+//  - 'FooWithHashCodeField' is from 'pkg/front_end/testcases/general/constants/various.dart'.
+// const bool fooWithHashCodeFieldEqual = fooWithHashCodeField1 == fooWithHashCodeField2;
+//                                                              ^
+// pkg/front_end/testcases/general/constants/various.dart:144:12: Context: While analyzing:
+// const bool fooWithHashCodeFieldEqual = fooWithHashCodeField1 == fooWithHashCodeField2;
+//            ^
+//
+// pkg/front_end/testcases/general/constants/various.dart:151:26: Error: Constant evaluation error:
 // const int circularity1 = circularity2;
 //                          ^
-// pkg/front_end/testcases/general/constants/various.dart:135:26: Context: Constant expression depends on itself.
+// pkg/front_end/testcases/general/constants/various.dart:151:26: Context: Constant expression depends on itself.
 // const int circularity1 = circularity2;
 //                          ^
-// pkg/front_end/testcases/general/constants/various.dart:135:11: Context: While analyzing:
+// pkg/front_end/testcases/general/constants/various.dart:151:11: Context: While analyzing:
 // const int circularity1 = circularity2;
 //           ^
 //
-// pkg/front_end/testcases/general/constants/various.dart:148:7: Error: Constant evaluation error:
+// pkg/front_end/testcases/general/constants/various.dart:164:7: Error: Constant evaluation error:
 // const ConstClassWithFailingAssertWithEmptyMessage();
 //       ^
-// pkg/front_end/testcases/general/constants/various.dart:144:64: Context: This assertion failed with message: (empty)
+// pkg/front_end/testcases/general/constants/various.dart:160:64: Context: This assertion failed with message: (empty)
 //   const ConstClassWithFailingAssertWithEmptyMessage() : assert(false, "");
 //                                                                ^
 //
-// pkg/front_end/testcases/general/constants/various.dart:185:7: Error: Constant evaluation error:
+// pkg/front_end/testcases/general/constants/various.dart:201:7: Error: Constant evaluation error:
 // const ConstClassWithFinalFields2();
 //       ^
-// pkg/front_end/testcases/general/constants/various.dart:181:14: Context: The invocation of 'x' is not allowed in a constant expression.
+// pkg/front_end/testcases/general/constants/various.dart:197:14: Context: The invocation of 'x' is not allowed in a constant expression.
 //   final z2 = x;
 //              ^
 //
-// pkg/front_end/testcases/general/constants/various.dart:203:35: Error: Constant evaluation error:
+// pkg/front_end/testcases/general/constants/various.dart:219:35: Error: Constant evaluation error:
 // const bool.fromEnvironment("foo") ? id1 : willBecomeNull;
 //                                   ^
-// pkg/front_end/testcases/general/constants/various.dart:203:35: Context: Expected constant 'null' to be of type 'int Function(int)', but was of type 'Null'.
+// pkg/front_end/testcases/general/constants/various.dart:219:35: Context: Expected constant 'null' to be of type 'int Function(int)', but was of type 'Null'.
 // const bool.fromEnvironment("foo") ? id1 : willBecomeNull;
 //                                   ^
-// pkg/front_end/testcases/general/constants/various.dart:202:25: Context: While analyzing:
+// pkg/front_end/testcases/general/constants/various.dart:218:25: Context: While analyzing:
 // const int Function(int) willBecomeNullToo =
 //                         ^
 //
@@ -246,6 +257,14 @@
     : self::Foo::x = x, self::Foo::y = "hello".{core::String::length}{core::int}, super core::Object::•()
     ;
 }
+class FooWithHashCodeField extends core::Object /*hasConstConstructor*/  {
+  final field core::int x;
+  final field core::int y;
+  final field core::int hashCode;
+  const constructor •(core::int x) → self::FooWithHashCodeField
+    : self::FooWithHashCodeField::x = x, self::FooWithHashCodeField::y = "hello".{core::String::length}{core::int}, self::FooWithHashCodeField::hashCode = x.{core::num::*}(42){(core::num) → core::int}, super core::Object::•()
+    ;
+}
 class ExtendsFoo1 extends self::Foo {
   synthetic constructor •() → self::ExtendsFoo1
     : invalid-initializer
@@ -253,7 +272,7 @@
 }
 class ExtendsFoo2 extends self::Foo /*hasConstConstructor*/  {
   const constructor •() → self::ExtendsFoo2
-    : final dynamic #t1 = invalid-expression "pkg/front_end/testcases/general/constants/various.dart:121:9: Error: The superclass, 'Foo', has no unnamed constructor that takes no arguments.
+    : final dynamic #t1 = invalid-expression "pkg/front_end/testcases/general/constants/various.dart:131:9: Error: The superclass, 'Foo', has no unnamed constructor that takes no arguments.
   const ExtendsFoo2();
         ^^^^^^^^^^^"
     ;
@@ -392,34 +411,39 @@
 const y4 = y++;
            ^";
 static field self::AbstractClassWithConstructor abstractClassWithConstructor = invalid-expression "The class 'AbstractClassWithConstructor' is abstract and can't be instantiated.";
-static const field self::ExtendsFoo1 extendsFoo1 = invalid-expression "pkg/front_end/testcases/general/constants/various.dart:118:39: Error: Cannot invoke a non-'const' constructor where a const expression is expected.
+static const field self::ExtendsFoo1 extendsFoo1 = invalid-expression "pkg/front_end/testcases/general/constants/various.dart:128:39: Error: Cannot invoke a non-'const' constructor where a const expression is expected.
 Try using a constructor or factory that is 'const'.
 const ExtendsFoo1 extendsFoo1 = const ExtendsFoo1();
                                       ^^^^^^^^^^^";
-static const field self::ExtendsFoo2 extendsFoo2 = invalid-expression "pkg/front_end/testcases/general/constants/various.dart:121:9: Error: The superclass, 'Foo', has no unnamed constructor that takes no arguments.
+static const field self::ExtendsFoo2 extendsFoo2 = invalid-expression "pkg/front_end/testcases/general/constants/various.dart:131:9: Error: The superclass, 'Foo', has no unnamed constructor that takes no arguments.
   const ExtendsFoo2();
         ^^^^^^^^^^^";
 static const field self::Foo foo1 = #C13;
 static const field self::Foo foo2 = #C13;
 static const field core::bool foosIdentical = #C2;
 static const field core::bool foosEqual = #C2;
-static const field core::Symbol barFoo = #C14;
-static const field core::Symbol barFooEqual = #C15;
-static const field core::Symbol tripleShiftSymbol = #C16;
-static const field core::Symbol symbolWithDots = #C17;
+static const field self::FooWithHashCodeField fooWithHashCodeField1 = #C15;
+static const field self::FooWithHashCodeField fooWithHashCodeField2 = #C15;
+static const field core::bool fooWithHashCodeFieldIdentical = #C2;
+static const field core::bool fooWithHashCodeFieldEqual = invalid-expression "Binary operator '==' requires receiver constant 'FooWithHashCodeField {x: 42, y: 5, hashCode: 1764}' of a type with primitive equality or type 'double', but was of type 'FooWithHashCodeField'.
+ - 'FooWithHashCodeField' is from 'pkg/front_end/testcases/general/constants/various.dart'.";
+static const field core::Symbol barFoo = #C16;
+static const field core::Symbol barFooEqual = #C17;
+static const field core::Symbol tripleShiftSymbol = #C18;
+static const field core::Symbol symbolWithDots = #C19;
 static const field core::int circularity1 = invalid-expression "Constant expression depends on itself.";
 static const field core::int circularity2 = invalid-expression "Constant expression depends on itself.";
 static const field core::int circularity3 = invalid-expression "Constant expression depends on itself.";
 static const field core::int circularity4 = invalid-expression "Constant expression depends on itself.";
-static const field invalid-type function_const = invalid-expression "pkg/front_end/testcases/general/constants/various.dart:140:24: Error: Not a constant expression.
+static const field invalid-type function_const = invalid-expression "pkg/front_end/testcases/general/constants/various.dart:156:24: Error: Not a constant expression.
 const function_const = () {};
                        ^^";
 static field () → Null function_var = () → Null {};
 static field self::ConstClassWithFailingAssertWithEmptyMessage failedAssertEmptyMessage = invalid-expression "This assertion failed with message: (empty)";
-static const field self::ClassWithTypeArguments<dynamic, dynamic, dynamic> classWithTypeArguments1 = #C18;
-static const field self::ClassWithTypeArguments<dynamic, dynamic, dynamic> classWithTypeArguments2 = #C19;
+static const field self::ClassWithTypeArguments<dynamic, dynamic, dynamic> classWithTypeArguments1 = #C20;
+static const field self::ClassWithTypeArguments<dynamic, dynamic, dynamic> classWithTypeArguments2 = #C21;
 static const field core::bool classWithTypeArgumentsIdentical = #C1;
-static field self::ClassWithNonEmptyConstConstructor classWithNonEmptyConstConstructor = invalid-expression "pkg/front_end/testcases/general/constants/various.dart:168:7: Error: Cannot invoke a non-'const' constructor where a const expression is expected.
+static field self::ClassWithNonEmptyConstConstructor classWithNonEmptyConstConstructor = invalid-expression "pkg/front_end/testcases/general/constants/various.dart:184:7: Error: Cannot invoke a non-'const' constructor where a const expression is expected.
 Try using a constructor or factory that is 'const'.
 const ClassWithNonEmptyConstConstructor();
       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^";
@@ -432,16 +456,16 @@
 static const field core::bool zeroPointZeroEqualToZero = #C2;
 static const field core::bool zeroEqualToZeroPointZero = #C2;
 static const field core::bool nanEqual = #C1;
-static const field dynamic willBecomeNull = #C20;
+static const field dynamic willBecomeNull = #C22;
 static const field (core::int) → core::int willBecomeNullToo = invalid-expression "Expected constant 'null' to be of type 'int Function(int)', but was of type 'Null'.";
-static const field (core::int) → core::int partialInstantiation = #C22;
+static const field (core::int) → core::int partialInstantiation = #C24;
 static const field core::bool yBool = #C2;
 static const field core::bool zBool = #C1;
 static const field core::Object maybeInt = #C2;
 static const field core::bool isItInt = #C1;
 static const field core::Object maybeInt2 = #C2;
 static const field core::bool isItInt2 = #C1;
-static const field core::int? maybeInt3 = #C20;
+static const field core::int? maybeInt3 = #C22;
 static const field core::bool isItInt3 = #C1;
 static method id1<T extends core::Object? = dynamic>(self::id1::T% t) → self::id1::T%
   return t;
@@ -466,23 +490,26 @@
   #C11 = 1
   #C12 = 5
   #C13 = self::Foo {x:#C6, y:#C12}
-  #C14 = #Foo
-  #C15 = #Foo=
-  #C16 = #>>>
-  #C17 = #I.Have.Dots
-  #C18 = self::ClassWithTypeArguments<core::int, core::int, core::int> {}
-  #C19 = self::ClassWithTypeArguments<dynamic, dynamic, dynamic> {}
-  #C20 = null
-  #C21 = static-tearoff self::id1
-  #C22 = instantiation #C21 <core::int>
+  #C14 = 1764
+  #C15 = self::FooWithHashCodeField {x:#C6, y:#C12, hashCode:#C14}
+  #C16 = #Foo
+  #C17 = #Foo=
+  #C18 = #>>>
+  #C19 = #I.Have.Dots
+  #C20 = self::ClassWithTypeArguments<core::int, core::int, core::int> {}
+  #C21 = self::ClassWithTypeArguments<dynamic, dynamic, dynamic> {}
+  #C22 = null
+  #C23 = static-tearoff self::id1
+  #C24 = instantiation #C23 <core::int>
 }
 
 
 Constructor coverage from constants:
 org-dartlang-testcase:///various.dart:
-- ExtendsFoo2. (from org-dartlang-testcase:///various.dart:121:9)
+- ExtendsFoo2. (from org-dartlang-testcase:///various.dart:131:9)
 - Foo. (from org-dartlang-testcase:///various.dart:109:9)
 - Object. (from org-dartlang-sdk:///sdk/lib/core/object.dart)
-- ConstClassWithFailingAssertWithEmptyMessage. (from org-dartlang-testcase:///various.dart:144:9)
-- ClassWithTypeArguments. (from org-dartlang-testcase:///various.dart:151:9)
-- ConstClassWithFinalFields2. (from org-dartlang-testcase:///various.dart:177:9)
+- FooWithHashCodeField. (from org-dartlang-testcase:///various.dart:118:9)
+- ConstClassWithFailingAssertWithEmptyMessage. (from org-dartlang-testcase:///various.dart:160:9)
+- ClassWithTypeArguments. (from org-dartlang-testcase:///various.dart:167:9)
+- ConstClassWithFinalFields2. (from org-dartlang-testcase:///various.dart:193:9)
diff --git a/pkg/front_end/testcases/general/constants/various.dart.strong.modular.expect b/pkg/front_end/testcases/general/constants/various.dart.strong.modular.expect
index 8e88044..01d5b41 100644
--- a/pkg/front_end/testcases/general/constants/various.dart.strong.modular.expect
+++ b/pkg/front_end/testcases/general/constants/various.dart.strong.modular.expect
@@ -2,7 +2,7 @@
 //
 // Problems in library:
 //
-// pkg/front_end/testcases/general/constants/various.dart:162:3: Error: A const constructor can't have a body.
+// pkg/front_end/testcases/general/constants/various.dart:178:3: Error: A const constructor can't have a body.
 // Try removing either the 'const' keyword or the body.
 //   const ClassWithNonEmptyConstConstructor() {
 //   ^^^^^
@@ -39,11 +39,11 @@
 // const y4 = y++;
 //            ^
 //
-// pkg/front_end/testcases/general/constants/various.dart:140:24: Error: Not a constant expression.
+// pkg/front_end/testcases/general/constants/various.dart:156:24: Error: Not a constant expression.
 // const function_const = () {};
 //                        ^^
 //
-// pkg/front_end/testcases/general/constants/various.dart:180:14: Error: Can't access 'this' in a field initializer to read 'y'.
+// pkg/front_end/testcases/general/constants/various.dart:196:14: Error: Can't access 'this' in a field initializer to read 'y'.
 //   final z1 = y;
 //              ^
 //
@@ -71,20 +71,20 @@
 //   @AbstractClassWithConstructor()
 //    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 //
-// pkg/front_end/testcases/general/constants/various.dart:118:39: Error: Cannot invoke a non-'const' constructor where a const expression is expected.
+// pkg/front_end/testcases/general/constants/various.dart:128:39: Error: Cannot invoke a non-'const' constructor where a const expression is expected.
 // Try using a constructor or factory that is 'const'.
 // const ExtendsFoo1 extendsFoo1 = const ExtendsFoo1();
 //                                       ^^^^^^^^^^^
 //
-// pkg/front_end/testcases/general/constants/various.dart:121:9: Error: The superclass, 'Foo', has no unnamed constructor that takes no arguments.
+// pkg/front_end/testcases/general/constants/various.dart:131:9: Error: The superclass, 'Foo', has no unnamed constructor that takes no arguments.
 //   const ExtendsFoo2();
 //         ^^^^^^^^^^^
 //
-// pkg/front_end/testcases/general/constants/various.dart:180:14: Error: Not a constant expression.
+// pkg/front_end/testcases/general/constants/various.dart:196:14: Error: Not a constant expression.
 //   final z1 = y;
 //              ^
 //
-// pkg/front_end/testcases/general/constants/various.dart:181:14: Error: Not a constant expression.
+// pkg/front_end/testcases/general/constants/various.dart:197:14: Error: Not a constant expression.
 //   final z2 = x;
 //              ^
 //
@@ -92,7 +92,7 @@
 // const AbstractClassWithConstructor();
 //       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 //
-// pkg/front_end/testcases/general/constants/various.dart:168:7: Error: Cannot invoke a non-'const' constructor where a const expression is expected.
+// pkg/front_end/testcases/general/constants/various.dart:184:7: Error: Cannot invoke a non-'const' constructor where a const expression is expected.
 // Try using a constructor or factory that is 'const'.
 // const ClassWithNonEmptyConstConstructor();
 //       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -107,7 +107,7 @@
 //   Object bar;
 //          ^^^
 //
-// pkg/front_end/testcases/general/constants/various.dart:114:7: Error: The superclass, 'Foo', has no unnamed constructor that takes no arguments.
+// pkg/front_end/testcases/general/constants/various.dart:124:7: Error: The superclass, 'Foo', has no unnamed constructor that takes no arguments.
 // class ExtendsFoo1 extends Foo {
 //       ^
 //
@@ -181,37 +181,48 @@
 // const x4 = x++;
 //       ^
 //
-// pkg/front_end/testcases/general/constants/various.dart:135:26: Error: Constant evaluation error:
+// pkg/front_end/testcases/general/constants/various.dart:144:62: Error: Constant evaluation error:
+// const bool fooWithHashCodeFieldEqual = fooWithHashCodeField1 == fooWithHashCodeField2;
+//                                                              ^
+// pkg/front_end/testcases/general/constants/various.dart:144:62: Context: Binary operator '==' requires receiver constant 'FooWithHashCodeField {x: 42, y: 5, hashCode: 1764}' of a type with primitive equality or type 'double', but was of type 'FooWithHashCodeField'.
+//  - 'FooWithHashCodeField' is from 'pkg/front_end/testcases/general/constants/various.dart'.
+// const bool fooWithHashCodeFieldEqual = fooWithHashCodeField1 == fooWithHashCodeField2;
+//                                                              ^
+// pkg/front_end/testcases/general/constants/various.dart:144:12: Context: While analyzing:
+// const bool fooWithHashCodeFieldEqual = fooWithHashCodeField1 == fooWithHashCodeField2;
+//            ^
+//
+// pkg/front_end/testcases/general/constants/various.dart:151:26: Error: Constant evaluation error:
 // const int circularity1 = circularity2;
 //                          ^
-// pkg/front_end/testcases/general/constants/various.dart:135:26: Context: Constant expression depends on itself.
+// pkg/front_end/testcases/general/constants/various.dart:151:26: Context: Constant expression depends on itself.
 // const int circularity1 = circularity2;
 //                          ^
-// pkg/front_end/testcases/general/constants/various.dart:135:11: Context: While analyzing:
+// pkg/front_end/testcases/general/constants/various.dart:151:11: Context: While analyzing:
 // const int circularity1 = circularity2;
 //           ^
 //
-// pkg/front_end/testcases/general/constants/various.dart:148:7: Error: Constant evaluation error:
+// pkg/front_end/testcases/general/constants/various.dart:164:7: Error: Constant evaluation error:
 // const ConstClassWithFailingAssertWithEmptyMessage();
 //       ^
-// pkg/front_end/testcases/general/constants/various.dart:144:64: Context: This assertion failed with message: (empty)
+// pkg/front_end/testcases/general/constants/various.dart:160:64: Context: This assertion failed with message: (empty)
 //   const ConstClassWithFailingAssertWithEmptyMessage() : assert(false, "");
 //                                                                ^
 //
-// pkg/front_end/testcases/general/constants/various.dart:185:7: Error: Constant evaluation error:
+// pkg/front_end/testcases/general/constants/various.dart:201:7: Error: Constant evaluation error:
 // const ConstClassWithFinalFields2();
 //       ^
-// pkg/front_end/testcases/general/constants/various.dart:181:14: Context: The invocation of 'x' is not allowed in a constant expression.
+// pkg/front_end/testcases/general/constants/various.dart:197:14: Context: The invocation of 'x' is not allowed in a constant expression.
 //   final z2 = x;
 //              ^
 //
-// pkg/front_end/testcases/general/constants/various.dart:203:35: Error: Constant evaluation error:
+// pkg/front_end/testcases/general/constants/various.dart:219:35: Error: Constant evaluation error:
 // const bool.fromEnvironment("foo") ? id1 : willBecomeNull;
 //                                   ^
-// pkg/front_end/testcases/general/constants/various.dart:203:35: Context: Expected constant 'null' to be of type 'int Function(int)', but was of type 'Null'.
+// pkg/front_end/testcases/general/constants/various.dart:219:35: Context: Expected constant 'null' to be of type 'int Function(int)', but was of type 'Null'.
 // const bool.fromEnvironment("foo") ? id1 : willBecomeNull;
 //                                   ^
-// pkg/front_end/testcases/general/constants/various.dart:202:25: Context: While analyzing:
+// pkg/front_end/testcases/general/constants/various.dart:218:25: Context: While analyzing:
 // const int Function(int) willBecomeNullToo =
 //                         ^
 //
@@ -246,6 +257,14 @@
     : self::Foo::x = x, self::Foo::y = "hello".{core::String::length}{core::int}, super core::Object::•()
     ;
 }
+class FooWithHashCodeField extends core::Object /*hasConstConstructor*/  {
+  final field core::int x;
+  final field core::int y;
+  final field core::int hashCode;
+  const constructor •(core::int x) → self::FooWithHashCodeField
+    : self::FooWithHashCodeField::x = x, self::FooWithHashCodeField::y = "hello".{core::String::length}{core::int}, self::FooWithHashCodeField::hashCode = x.{core::num::*}(42){(core::num) → core::int}, super core::Object::•()
+    ;
+}
 class ExtendsFoo1 extends self::Foo {
   synthetic constructor •() → self::ExtendsFoo1
     : invalid-initializer
@@ -253,7 +272,7 @@
 }
 class ExtendsFoo2 extends self::Foo /*hasConstConstructor*/  {
   const constructor •() → self::ExtendsFoo2
-    : final dynamic #t1 = invalid-expression "pkg/front_end/testcases/general/constants/various.dart:121:9: Error: The superclass, 'Foo', has no unnamed constructor that takes no arguments.
+    : final dynamic #t1 = invalid-expression "pkg/front_end/testcases/general/constants/various.dart:131:9: Error: The superclass, 'Foo', has no unnamed constructor that takes no arguments.
   const ExtendsFoo2();
         ^^^^^^^^^^^"
     ;
@@ -392,34 +411,39 @@
 const y4 = y++;
            ^";
 static field self::AbstractClassWithConstructor abstractClassWithConstructor = invalid-expression "The class 'AbstractClassWithConstructor' is abstract and can't be instantiated.";
-static const field self::ExtendsFoo1 extendsFoo1 = invalid-expression "pkg/front_end/testcases/general/constants/various.dart:118:39: Error: Cannot invoke a non-'const' constructor where a const expression is expected.
+static const field self::ExtendsFoo1 extendsFoo1 = invalid-expression "pkg/front_end/testcases/general/constants/various.dart:128:39: Error: Cannot invoke a non-'const' constructor where a const expression is expected.
 Try using a constructor or factory that is 'const'.
 const ExtendsFoo1 extendsFoo1 = const ExtendsFoo1();
                                       ^^^^^^^^^^^";
-static const field self::ExtendsFoo2 extendsFoo2 = invalid-expression "pkg/front_end/testcases/general/constants/various.dart:121:9: Error: The superclass, 'Foo', has no unnamed constructor that takes no arguments.
+static const field self::ExtendsFoo2 extendsFoo2 = invalid-expression "pkg/front_end/testcases/general/constants/various.dart:131:9: Error: The superclass, 'Foo', has no unnamed constructor that takes no arguments.
   const ExtendsFoo2();
         ^^^^^^^^^^^";
 static const field self::Foo foo1 = #C13;
 static const field self::Foo foo2 = #C13;
 static const field core::bool foosIdentical = #C2;
 static const field core::bool foosEqual = #C2;
-static const field core::Symbol barFoo = #C14;
-static const field core::Symbol barFooEqual = #C15;
-static const field core::Symbol tripleShiftSymbol = #C16;
-static const field core::Symbol symbolWithDots = #C17;
+static const field self::FooWithHashCodeField fooWithHashCodeField1 = #C15;
+static const field self::FooWithHashCodeField fooWithHashCodeField2 = #C15;
+static const field core::bool fooWithHashCodeFieldIdentical = #C2;
+static const field core::bool fooWithHashCodeFieldEqual = invalid-expression "Binary operator '==' requires receiver constant 'FooWithHashCodeField {x: 42, y: 5, hashCode: 1764}' of a type with primitive equality or type 'double', but was of type 'FooWithHashCodeField'.
+ - 'FooWithHashCodeField' is from 'pkg/front_end/testcases/general/constants/various.dart'.";
+static const field core::Symbol barFoo = #C16;
+static const field core::Symbol barFooEqual = #C17;
+static const field core::Symbol tripleShiftSymbol = #C18;
+static const field core::Symbol symbolWithDots = #C19;
 static const field core::int circularity1 = invalid-expression "Constant expression depends on itself.";
 static const field core::int circularity2 = invalid-expression "Constant expression depends on itself.";
 static const field core::int circularity3 = invalid-expression "Constant expression depends on itself.";
 static const field core::int circularity4 = invalid-expression "Constant expression depends on itself.";
-static const field invalid-type function_const = invalid-expression "pkg/front_end/testcases/general/constants/various.dart:140:24: Error: Not a constant expression.
+static const field invalid-type function_const = invalid-expression "pkg/front_end/testcases/general/constants/various.dart:156:24: Error: Not a constant expression.
 const function_const = () {};
                        ^^";
 static field () → Null function_var = () → Null {};
 static field self::ConstClassWithFailingAssertWithEmptyMessage failedAssertEmptyMessage = invalid-expression "This assertion failed with message: (empty)";
-static const field self::ClassWithTypeArguments<dynamic, dynamic, dynamic> classWithTypeArguments1 = #C18;
-static const field self::ClassWithTypeArguments<dynamic, dynamic, dynamic> classWithTypeArguments2 = #C19;
+static const field self::ClassWithTypeArguments<dynamic, dynamic, dynamic> classWithTypeArguments1 = #C20;
+static const field self::ClassWithTypeArguments<dynamic, dynamic, dynamic> classWithTypeArguments2 = #C21;
 static const field core::bool classWithTypeArgumentsIdentical = #C1;
-static field self::ClassWithNonEmptyConstConstructor classWithNonEmptyConstConstructor = invalid-expression "pkg/front_end/testcases/general/constants/various.dart:168:7: Error: Cannot invoke a non-'const' constructor where a const expression is expected.
+static field self::ClassWithNonEmptyConstConstructor classWithNonEmptyConstConstructor = invalid-expression "pkg/front_end/testcases/general/constants/various.dart:184:7: Error: Cannot invoke a non-'const' constructor where a const expression is expected.
 Try using a constructor or factory that is 'const'.
 const ClassWithNonEmptyConstConstructor();
       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^";
@@ -432,16 +456,16 @@
 static const field core::bool zeroPointZeroEqualToZero = #C2;
 static const field core::bool zeroEqualToZeroPointZero = #C2;
 static const field core::bool nanEqual = #C1;
-static const field dynamic willBecomeNull = #C20;
+static const field dynamic willBecomeNull = #C22;
 static const field (core::int) → core::int willBecomeNullToo = invalid-expression "Expected constant 'null' to be of type 'int Function(int)', but was of type 'Null'.";
-static const field (core::int) → core::int partialInstantiation = #C22;
+static const field (core::int) → core::int partialInstantiation = #C24;
 static const field core::bool yBool = #C2;
 static const field core::bool zBool = #C1;
 static const field core::Object maybeInt = #C2;
 static const field core::bool isItInt = #C1;
 static const field core::Object maybeInt2 = #C2;
 static const field core::bool isItInt2 = #C1;
-static const field core::int? maybeInt3 = #C20;
+static const field core::int? maybeInt3 = #C22;
 static const field core::bool isItInt3 = #C1;
 static method id1<T extends core::Object? = dynamic>(self::id1::T% t) → self::id1::T%
   return t;
@@ -466,23 +490,26 @@
   #C11 = 1
   #C12 = 5
   #C13 = self::Foo {x:#C6, y:#C12}
-  #C14 = #Foo
-  #C15 = #Foo=
-  #C16 = #>>>
-  #C17 = #I.Have.Dots
-  #C18 = self::ClassWithTypeArguments<core::int, core::int, core::int> {}
-  #C19 = self::ClassWithTypeArguments<dynamic, dynamic, dynamic> {}
-  #C20 = null
-  #C21 = static-tearoff self::id1
-  #C22 = instantiation #C21 <core::int>
+  #C14 = 1764
+  #C15 = self::FooWithHashCodeField {x:#C6, y:#C12, hashCode:#C14}
+  #C16 = #Foo
+  #C17 = #Foo=
+  #C18 = #>>>
+  #C19 = #I.Have.Dots
+  #C20 = self::ClassWithTypeArguments<core::int, core::int, core::int> {}
+  #C21 = self::ClassWithTypeArguments<dynamic, dynamic, dynamic> {}
+  #C22 = null
+  #C23 = static-tearoff self::id1
+  #C24 = instantiation #C23 <core::int>
 }
 
 
 Constructor coverage from constants:
 org-dartlang-testcase:///various.dart:
-- ExtendsFoo2. (from org-dartlang-testcase:///various.dart:121:9)
+- ExtendsFoo2. (from org-dartlang-testcase:///various.dart:131:9)
 - Foo. (from org-dartlang-testcase:///various.dart:109:9)
 - Object. (from org-dartlang-sdk:///sdk/lib/core/object.dart)
-- ConstClassWithFailingAssertWithEmptyMessage. (from org-dartlang-testcase:///various.dart:144:9)
-- ClassWithTypeArguments. (from org-dartlang-testcase:///various.dart:151:9)
-- ConstClassWithFinalFields2. (from org-dartlang-testcase:///various.dart:177:9)
+- FooWithHashCodeField. (from org-dartlang-testcase:///various.dart:118:9)
+- ConstClassWithFailingAssertWithEmptyMessage. (from org-dartlang-testcase:///various.dart:160:9)
+- ClassWithTypeArguments. (from org-dartlang-testcase:///various.dart:167:9)
+- ConstClassWithFinalFields2. (from org-dartlang-testcase:///various.dart:193:9)
diff --git a/pkg/front_end/testcases/general/constants/various.dart.strong.outline.expect b/pkg/front_end/testcases/general/constants/various.dart.strong.outline.expect
index ab0bb85..0d65ced 100644
--- a/pkg/front_end/testcases/general/constants/various.dart.strong.outline.expect
+++ b/pkg/front_end/testcases/general/constants/various.dart.strong.outline.expect
@@ -2,7 +2,7 @@
 //
 // Problems in library:
 //
-// pkg/front_end/testcases/general/constants/various.dart:162:3: Error: A const constructor can't have a body.
+// pkg/front_end/testcases/general/constants/various.dart:178:3: Error: A const constructor can't have a body.
 // Try removing either the 'const' keyword or the body.
 //   const ClassWithNonEmptyConstConstructor() {
 //   ^^^^^
@@ -39,11 +39,11 @@
 // const y4 = y++;
 //            ^
 //
-// pkg/front_end/testcases/general/constants/various.dart:140:24: Error: Not a constant expression.
+// pkg/front_end/testcases/general/constants/various.dart:156:24: Error: Not a constant expression.
 // const function_const = () {};
 //                        ^^
 //
-// pkg/front_end/testcases/general/constants/various.dart:180:14: Error: Can't access 'this' in a field initializer to read 'y'.
+// pkg/front_end/testcases/general/constants/various.dart:196:14: Error: Can't access 'this' in a field initializer to read 'y'.
 //   final z1 = y;
 //              ^
 //
@@ -71,20 +71,20 @@
 //   @AbstractClassWithConstructor()
 //    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 //
-// pkg/front_end/testcases/general/constants/various.dart:118:39: Error: Cannot invoke a non-'const' constructor where a const expression is expected.
+// pkg/front_end/testcases/general/constants/various.dart:128:39: Error: Cannot invoke a non-'const' constructor where a const expression is expected.
 // Try using a constructor or factory that is 'const'.
 // const ExtendsFoo1 extendsFoo1 = const ExtendsFoo1();
 //                                       ^^^^^^^^^^^
 //
-// pkg/front_end/testcases/general/constants/various.dart:121:9: Error: The superclass, 'Foo', has no unnamed constructor that takes no arguments.
+// pkg/front_end/testcases/general/constants/various.dart:131:9: Error: The superclass, 'Foo', has no unnamed constructor that takes no arguments.
 //   const ExtendsFoo2();
 //         ^^^^^^^^^^^
 //
-// pkg/front_end/testcases/general/constants/various.dart:180:14: Error: Not a constant expression.
+// pkg/front_end/testcases/general/constants/various.dart:196:14: Error: Not a constant expression.
 //   final z1 = y;
 //              ^
 //
-// pkg/front_end/testcases/general/constants/various.dart:181:14: Error: Not a constant expression.
+// pkg/front_end/testcases/general/constants/various.dart:197:14: Error: Not a constant expression.
 //   final z2 = x;
 //              ^
 //
@@ -117,13 +117,21 @@
     : self::Foo::x = x, self::Foo::y = "hello".{core::String::length}{core::int}, super core::Object::•()
     ;
 }
+class FooWithHashCodeField extends core::Object /*hasConstConstructor*/  {
+  final field core::int x;
+  final field core::int y;
+  final field core::int hashCode;
+  const constructor •(core::int x) → self::FooWithHashCodeField
+    : self::FooWithHashCodeField::x = x, self::FooWithHashCodeField::y = "hello".{core::String::length}{core::int}, self::FooWithHashCodeField::hashCode = x.{core::num::*}(42){(core::num) → core::int}, super core::Object::•()
+    ;
+}
 class ExtendsFoo1 extends self::Foo {
   synthetic constructor •() → self::ExtendsFoo1
     ;
 }
 class ExtendsFoo2 extends self::Foo /*hasConstConstructor*/  {
   const constructor •() → self::ExtendsFoo2
-    : final dynamic #t1 = invalid-expression "pkg/front_end/testcases/general/constants/various.dart:121:9: Error: The superclass, 'Foo', has no unnamed constructor that takes no arguments.
+    : final dynamic #t1 = invalid-expression "pkg/front_end/testcases/general/constants/various.dart:131:9: Error: The superclass, 'Foo', has no unnamed constructor that takes no arguments.
   const ExtendsFoo2();
         ^^^^^^^^^^^"
     ;
@@ -232,7 +240,7 @@
 const y4 = y++;
            ^" in #t10;
 static field self::AbstractClassWithConstructor abstractClassWithConstructor;
-static const field self::ExtendsFoo1 extendsFoo1 = invalid-expression "pkg/front_end/testcases/general/constants/various.dart:118:39: Error: Cannot invoke a non-'const' constructor where a const expression is expected.
+static const field self::ExtendsFoo1 extendsFoo1 = invalid-expression "pkg/front_end/testcases/general/constants/various.dart:128:39: Error: Cannot invoke a non-'const' constructor where a const expression is expected.
 Try using a constructor or factory that is 'const'.
 const ExtendsFoo1 extendsFoo1 = const ExtendsFoo1();
                                       ^^^^^^^^^^^";
@@ -241,6 +249,10 @@
 static const field self::Foo foo2 = const self::Foo::•(42);
 static const field core::bool foosIdentical = core::identical(self::foo1, self::foo2);
 static const field core::bool foosEqual = self::foo1 =={core::Object::==}{(core::Object) → core::bool} self::foo2;
+static const field self::FooWithHashCodeField fooWithHashCodeField1 = const self::FooWithHashCodeField::•(42);
+static const field self::FooWithHashCodeField fooWithHashCodeField2 = const self::FooWithHashCodeField::•(42);
+static const field core::bool fooWithHashCodeFieldIdentical = core::identical(self::fooWithHashCodeField1, self::fooWithHashCodeField2);
+static const field core::bool fooWithHashCodeFieldEqual = self::fooWithHashCodeField1 =={core::Object::==}{(core::Object) → core::bool} self::fooWithHashCodeField2;
 static const field core::Symbol barFoo = const _in::Symbol::•("Foo");
 static const field core::Symbol barFooEqual = const _in::Symbol::•("Foo=");
 static const field core::Symbol tripleShiftSymbol = const _in::Symbol::•(">>>");
@@ -249,7 +261,7 @@
 static const field core::int circularity2 = self::circularity3;
 static const field core::int circularity3 = self::circularity4;
 static const field core::int circularity4 = self::circularity1;
-static const field invalid-type function_const = invalid-expression "pkg/front_end/testcases/general/constants/various.dart:140:24: Error: Not a constant expression.
+static const field invalid-type function_const = invalid-expression "pkg/front_end/testcases/general/constants/various.dart:156:24: Error: Not a constant expression.
 const function_const = () {};
                        ^^";
 static field () → Null function_var;
@@ -288,6 +300,7 @@
 
 Extra constant evaluation status:
 Evaluated: InstanceGet @ org-dartlang-testcase:///various.dart:111:26 -> IntConstant(5)
+Evaluated: InstanceGet @ org-dartlang-testcase:///various.dart:120:26 -> IntConstant(5)
 Evaluated: FactoryConstructorInvocation @ org-dartlang-testcase:///various.dart:6:31 -> BoolConstant(false)
 Evaluated: FactoryConstructorInvocation @ org-dartlang-testcase:///various.dart:7:30 -> BoolConstant(false)
 Evaluated: LogicalExpression @ org-dartlang-testcase:///various.dart:20:29 -> BoolConstant(true)
@@ -323,33 +336,38 @@
 Evaluated: VariableGet @ org-dartlang-testcase:///various.dart:84:12 -> IntConstant(1)
 Evaluated: StaticGet @ org-dartlang-testcase:///various.dart:85:12 -> IntConstant(1)
 Evaluated: VariableGet @ org-dartlang-testcase:///various.dart:85:12 -> IntConstant(1)
-Evaluated: ConstructorInvocation @ org-dartlang-testcase:///various.dart:126:24 -> InstanceConstant(const Foo{Foo.x: 42, Foo.y: 5})
-Evaluated: ConstructorInvocation @ org-dartlang-testcase:///various.dart:127:24 -> InstanceConstant(const Foo{Foo.x: 42, Foo.y: 5})
-Evaluated: StaticInvocation @ org-dartlang-testcase:///various.dart:128:28 -> BoolConstant(true)
-Evaluated: EqualsCall @ org-dartlang-testcase:///various.dart:129:29 -> BoolConstant(true)
-Evaluated: ConstructorInvocation @ org-dartlang-testcase:///various.dart:130:29 -> SymbolConstant(#Foo)
-Evaluated: ConstructorInvocation @ org-dartlang-testcase:///various.dart:131:34 -> SymbolConstant(#Foo=)
-Evaluated: ConstructorInvocation @ org-dartlang-testcase:///various.dart:132:40 -> SymbolConstant(#>>>)
-Evaluated: ConstructorInvocation @ org-dartlang-testcase:///various.dart:133:37 -> SymbolConstant(#I.Have.Dots)
-Evaluated: ConstructorInvocation @ org-dartlang-testcase:///various.dart:155:7 -> InstanceConstant(const ClassWithTypeArguments<int, int, int>{})
-Evaluated: ConstructorInvocation @ org-dartlang-testcase:///various.dart:157:7 -> InstanceConstant(const ClassWithTypeArguments<dynamic, dynamic, dynamic>{})
-Evaluated: StaticInvocation @ org-dartlang-testcase:///various.dart:159:1 -> BoolConstant(false)
-Evaluated: StaticInvocation @ org-dartlang-testcase:///various.dart:187:32 -> BoolConstant(true)
-Evaluated: StaticInvocation @ org-dartlang-testcase:///various.dart:188:38 -> BoolConstant(false)
-Evaluated: StaticInvocation @ org-dartlang-testcase:///various.dart:189:38 -> BoolConstant(false)
-Evaluated: StaticInvocation @ org-dartlang-testcase:///various.dart:190:22 -> BoolConstant(true)
-Evaluated: EqualsCall @ org-dartlang-testcase:///various.dart:192:32 -> BoolConstant(true)
-Evaluated: EqualsCall @ org-dartlang-testcase:///various.dart:193:38 -> BoolConstant(true)
-Evaluated: EqualsCall @ org-dartlang-testcase:///various.dart:194:36 -> BoolConstant(true)
-Evaluated: EqualsCall @ org-dartlang-testcase:///various.dart:195:24 -> BoolConstant(false)
-Evaluated: ConditionalExpression @ org-dartlang-testcase:///various.dart:200:66 -> NullConstant(null)
-Evaluated: ConditionalExpression @ org-dartlang-testcase:///various.dart:203:35 -> NullConstant(null)
-Evaluated: AsExpression @ org-dartlang-testcase:///various.dart:205:35 -> InstantiationConstant(id1<int>)
-Evaluated: Not @ org-dartlang-testcase:///various.dart:208:20 -> BoolConstant(false)
-Evaluated: ConditionalExpression @ org-dartlang-testcase:///various.dart:210:46 -> BoolConstant(true)
-Evaluated: ConditionalExpression @ org-dartlang-testcase:///various.dart:211:38 -> BoolConstant(false)
-Evaluated: ConditionalExpression @ org-dartlang-testcase:///various.dart:212:25 -> BoolConstant(true)
-Evaluated: ConditionalExpression @ org-dartlang-testcase:///various.dart:213:40 -> BoolConstant(false)
-Evaluated: ConditionalExpression @ org-dartlang-testcase:///various.dart:214:25 -> NullConstant(null)
-Evaluated: ConditionalExpression @ org-dartlang-testcase:///various.dart:215:40 -> BoolConstant(false)
-Extra constant evaluation: evaluated: 146, effectively constant: 65
+Evaluated: ConstructorInvocation @ org-dartlang-testcase:///various.dart:136:24 -> InstanceConstant(const Foo{Foo.x: 42, Foo.y: 5})
+Evaluated: ConstructorInvocation @ org-dartlang-testcase:///various.dart:137:24 -> InstanceConstant(const Foo{Foo.x: 42, Foo.y: 5})
+Evaluated: StaticInvocation @ org-dartlang-testcase:///various.dart:138:28 -> BoolConstant(true)
+Evaluated: EqualsCall @ org-dartlang-testcase:///various.dart:139:29 -> BoolConstant(true)
+Evaluated: ConstructorInvocation @ org-dartlang-testcase:///various.dart:141:58 -> InstanceConstant(const FooWithHashCodeField{FooWithHashCodeField.x: 42, FooWithHashCodeField.y: 5, FooWithHashCodeField.hashCode: 1764})
+Evaluated: ConstructorInvocation @ org-dartlang-testcase:///various.dart:142:58 -> InstanceConstant(const FooWithHashCodeField{FooWithHashCodeField.x: 42, FooWithHashCodeField.y: 5, FooWithHashCodeField.hashCode: 1764})
+Evaluated: StaticInvocation @ org-dartlang-testcase:///various.dart:143:44 -> BoolConstant(true)
+Evaluated: StaticGet @ org-dartlang-testcase:///various.dart:144:40 -> InstanceConstant(const FooWithHashCodeField{FooWithHashCodeField.x: 42, FooWithHashCodeField.y: 5, FooWithHashCodeField.hashCode: 1764})
+Evaluated: StaticGet @ org-dartlang-testcase:///various.dart:144:65 -> InstanceConstant(const FooWithHashCodeField{FooWithHashCodeField.x: 42, FooWithHashCodeField.y: 5, FooWithHashCodeField.hashCode: 1764})
+Evaluated: ConstructorInvocation @ org-dartlang-testcase:///various.dart:146:29 -> SymbolConstant(#Foo)
+Evaluated: ConstructorInvocation @ org-dartlang-testcase:///various.dart:147:34 -> SymbolConstant(#Foo=)
+Evaluated: ConstructorInvocation @ org-dartlang-testcase:///various.dart:148:40 -> SymbolConstant(#>>>)
+Evaluated: ConstructorInvocation @ org-dartlang-testcase:///various.dart:149:37 -> SymbolConstant(#I.Have.Dots)
+Evaluated: ConstructorInvocation @ org-dartlang-testcase:///various.dart:171:7 -> InstanceConstant(const ClassWithTypeArguments<int, int, int>{})
+Evaluated: ConstructorInvocation @ org-dartlang-testcase:///various.dart:173:7 -> InstanceConstant(const ClassWithTypeArguments<dynamic, dynamic, dynamic>{})
+Evaluated: StaticInvocation @ org-dartlang-testcase:///various.dart:175:1 -> BoolConstant(false)
+Evaluated: StaticInvocation @ org-dartlang-testcase:///various.dart:203:32 -> BoolConstant(true)
+Evaluated: StaticInvocation @ org-dartlang-testcase:///various.dart:204:38 -> BoolConstant(false)
+Evaluated: StaticInvocation @ org-dartlang-testcase:///various.dart:205:38 -> BoolConstant(false)
+Evaluated: StaticInvocation @ org-dartlang-testcase:///various.dart:206:22 -> BoolConstant(true)
+Evaluated: EqualsCall @ org-dartlang-testcase:///various.dart:208:32 -> BoolConstant(true)
+Evaluated: EqualsCall @ org-dartlang-testcase:///various.dart:209:38 -> BoolConstant(true)
+Evaluated: EqualsCall @ org-dartlang-testcase:///various.dart:210:36 -> BoolConstant(true)
+Evaluated: EqualsCall @ org-dartlang-testcase:///various.dart:211:24 -> BoolConstant(false)
+Evaluated: ConditionalExpression @ org-dartlang-testcase:///various.dart:216:66 -> NullConstant(null)
+Evaluated: ConditionalExpression @ org-dartlang-testcase:///various.dart:219:35 -> NullConstant(null)
+Evaluated: AsExpression @ org-dartlang-testcase:///various.dart:221:35 -> InstantiationConstant(id1<int>)
+Evaluated: Not @ org-dartlang-testcase:///various.dart:224:20 -> BoolConstant(false)
+Evaluated: ConditionalExpression @ org-dartlang-testcase:///various.dart:226:46 -> BoolConstant(true)
+Evaluated: ConditionalExpression @ org-dartlang-testcase:///various.dart:227:38 -> BoolConstant(false)
+Evaluated: ConditionalExpression @ org-dartlang-testcase:///various.dart:228:25 -> BoolConstant(true)
+Evaluated: ConditionalExpression @ org-dartlang-testcase:///various.dart:229:40 -> BoolConstant(false)
+Evaluated: ConditionalExpression @ org-dartlang-testcase:///various.dart:230:25 -> NullConstant(null)
+Evaluated: ConditionalExpression @ org-dartlang-testcase:///various.dart:231:40 -> BoolConstant(false)
+Extra constant evaluation: evaluated: 156, effectively constant: 71
diff --git a/pkg/front_end/testcases/general/constants/various.dart.strong.transformed.expect b/pkg/front_end/testcases/general/constants/various.dart.strong.transformed.expect
index 27cedb6..a869efc 100644
--- a/pkg/front_end/testcases/general/constants/various.dart.strong.transformed.expect
+++ b/pkg/front_end/testcases/general/constants/various.dart.strong.transformed.expect
@@ -2,7 +2,7 @@
 //
 // Problems in library:
 //
-// pkg/front_end/testcases/general/constants/various.dart:162:3: Error: A const constructor can't have a body.
+// pkg/front_end/testcases/general/constants/various.dart:178:3: Error: A const constructor can't have a body.
 // Try removing either the 'const' keyword or the body.
 //   const ClassWithNonEmptyConstConstructor() {
 //   ^^^^^
@@ -39,11 +39,11 @@
 // const y4 = y++;
 //            ^
 //
-// pkg/front_end/testcases/general/constants/various.dart:140:24: Error: Not a constant expression.
+// pkg/front_end/testcases/general/constants/various.dart:156:24: Error: Not a constant expression.
 // const function_const = () {};
 //                        ^^
 //
-// pkg/front_end/testcases/general/constants/various.dart:180:14: Error: Can't access 'this' in a field initializer to read 'y'.
+// pkg/front_end/testcases/general/constants/various.dart:196:14: Error: Can't access 'this' in a field initializer to read 'y'.
 //   final z1 = y;
 //              ^
 //
@@ -71,20 +71,20 @@
 //   @AbstractClassWithConstructor()
 //    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 //
-// pkg/front_end/testcases/general/constants/various.dart:118:39: Error: Cannot invoke a non-'const' constructor where a const expression is expected.
+// pkg/front_end/testcases/general/constants/various.dart:128:39: Error: Cannot invoke a non-'const' constructor where a const expression is expected.
 // Try using a constructor or factory that is 'const'.
 // const ExtendsFoo1 extendsFoo1 = const ExtendsFoo1();
 //                                       ^^^^^^^^^^^
 //
-// pkg/front_end/testcases/general/constants/various.dart:121:9: Error: The superclass, 'Foo', has no unnamed constructor that takes no arguments.
+// pkg/front_end/testcases/general/constants/various.dart:131:9: Error: The superclass, 'Foo', has no unnamed constructor that takes no arguments.
 //   const ExtendsFoo2();
 //         ^^^^^^^^^^^
 //
-// pkg/front_end/testcases/general/constants/various.dart:180:14: Error: Not a constant expression.
+// pkg/front_end/testcases/general/constants/various.dart:196:14: Error: Not a constant expression.
 //   final z1 = y;
 //              ^
 //
-// pkg/front_end/testcases/general/constants/various.dart:181:14: Error: Not a constant expression.
+// pkg/front_end/testcases/general/constants/various.dart:197:14: Error: Not a constant expression.
 //   final z2 = x;
 //              ^
 //
@@ -92,7 +92,7 @@
 // const AbstractClassWithConstructor();
 //       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 //
-// pkg/front_end/testcases/general/constants/various.dart:168:7: Error: Cannot invoke a non-'const' constructor where a const expression is expected.
+// pkg/front_end/testcases/general/constants/various.dart:184:7: Error: Cannot invoke a non-'const' constructor where a const expression is expected.
 // Try using a constructor or factory that is 'const'.
 // const ClassWithNonEmptyConstConstructor();
 //       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -107,7 +107,7 @@
 //   Object bar;
 //          ^^^
 //
-// pkg/front_end/testcases/general/constants/various.dart:114:7: Error: The superclass, 'Foo', has no unnamed constructor that takes no arguments.
+// pkg/front_end/testcases/general/constants/various.dart:124:7: Error: The superclass, 'Foo', has no unnamed constructor that takes no arguments.
 // class ExtendsFoo1 extends Foo {
 //       ^
 //
@@ -181,37 +181,48 @@
 // const x4 = x++;
 //       ^
 //
-// pkg/front_end/testcases/general/constants/various.dart:135:26: Error: Constant evaluation error:
+// pkg/front_end/testcases/general/constants/various.dart:144:62: Error: Constant evaluation error:
+// const bool fooWithHashCodeFieldEqual = fooWithHashCodeField1 == fooWithHashCodeField2;
+//                                                              ^
+// pkg/front_end/testcases/general/constants/various.dart:144:62: Context: Binary operator '==' requires receiver constant 'FooWithHashCodeField {x: 42, y: 5, hashCode: 1764}' of a type with primitive equality or type 'double', but was of type 'FooWithHashCodeField'.
+//  - 'FooWithHashCodeField' is from 'pkg/front_end/testcases/general/constants/various.dart'.
+// const bool fooWithHashCodeFieldEqual = fooWithHashCodeField1 == fooWithHashCodeField2;
+//                                                              ^
+// pkg/front_end/testcases/general/constants/various.dart:144:12: Context: While analyzing:
+// const bool fooWithHashCodeFieldEqual = fooWithHashCodeField1 == fooWithHashCodeField2;
+//            ^
+//
+// pkg/front_end/testcases/general/constants/various.dart:151:26: Error: Constant evaluation error:
 // const int circularity1 = circularity2;
 //                          ^
-// pkg/front_end/testcases/general/constants/various.dart:135:26: Context: Constant expression depends on itself.
+// pkg/front_end/testcases/general/constants/various.dart:151:26: Context: Constant expression depends on itself.
 // const int circularity1 = circularity2;
 //                          ^
-// pkg/front_end/testcases/general/constants/various.dart:135:11: Context: While analyzing:
+// pkg/front_end/testcases/general/constants/various.dart:151:11: Context: While analyzing:
 // const int circularity1 = circularity2;
 //           ^
 //
-// pkg/front_end/testcases/general/constants/various.dart:148:7: Error: Constant evaluation error:
+// pkg/front_end/testcases/general/constants/various.dart:164:7: Error: Constant evaluation error:
 // const ConstClassWithFailingAssertWithEmptyMessage();
 //       ^
-// pkg/front_end/testcases/general/constants/various.dart:144:64: Context: This assertion failed with message: (empty)
+// pkg/front_end/testcases/general/constants/various.dart:160:64: Context: This assertion failed with message: (empty)
 //   const ConstClassWithFailingAssertWithEmptyMessage() : assert(false, "");
 //                                                                ^
 //
-// pkg/front_end/testcases/general/constants/various.dart:185:7: Error: Constant evaluation error:
+// pkg/front_end/testcases/general/constants/various.dart:201:7: Error: Constant evaluation error:
 // const ConstClassWithFinalFields2();
 //       ^
-// pkg/front_end/testcases/general/constants/various.dart:181:14: Context: The invocation of 'x' is not allowed in a constant expression.
+// pkg/front_end/testcases/general/constants/various.dart:197:14: Context: The invocation of 'x' is not allowed in a constant expression.
 //   final z2 = x;
 //              ^
 //
-// pkg/front_end/testcases/general/constants/various.dart:203:35: Error: Constant evaluation error:
+// pkg/front_end/testcases/general/constants/various.dart:219:35: Error: Constant evaluation error:
 // const bool.fromEnvironment("foo") ? id1 : willBecomeNull;
 //                                   ^
-// pkg/front_end/testcases/general/constants/various.dart:203:35: Context: Expected constant 'null' to be of type 'int Function(int)', but was of type 'Null'.
+// pkg/front_end/testcases/general/constants/various.dart:219:35: Context: Expected constant 'null' to be of type 'int Function(int)', but was of type 'Null'.
 // const bool.fromEnvironment("foo") ? id1 : willBecomeNull;
 //                                   ^
-// pkg/front_end/testcases/general/constants/various.dart:202:25: Context: While analyzing:
+// pkg/front_end/testcases/general/constants/various.dart:218:25: Context: While analyzing:
 // const int Function(int) willBecomeNullToo =
 //                         ^
 //
@@ -246,6 +257,14 @@
     : self::Foo::x = x, self::Foo::y = "hello".{core::String::length}{core::int}, super core::Object::•()
     ;
 }
+class FooWithHashCodeField extends core::Object /*hasConstConstructor*/  {
+  final field core::int x;
+  final field core::int y;
+  final field core::int hashCode;
+  const constructor •(core::int x) → self::FooWithHashCodeField
+    : self::FooWithHashCodeField::x = x, self::FooWithHashCodeField::y = "hello".{core::String::length}{core::int}, self::FooWithHashCodeField::hashCode = x.{core::num::*}(42){(core::num) → core::int}, super core::Object::•()
+    ;
+}
 class ExtendsFoo1 extends self::Foo {
   synthetic constructor •() → self::ExtendsFoo1
     : invalid-initializer
@@ -253,7 +272,7 @@
 }
 class ExtendsFoo2 extends self::Foo /*hasConstConstructor*/  {
   const constructor •() → self::ExtendsFoo2
-    : final dynamic #t1 = invalid-expression "pkg/front_end/testcases/general/constants/various.dart:121:9: Error: The superclass, 'Foo', has no unnamed constructor that takes no arguments.
+    : final dynamic #t1 = invalid-expression "pkg/front_end/testcases/general/constants/various.dart:131:9: Error: The superclass, 'Foo', has no unnamed constructor that takes no arguments.
   const ExtendsFoo2();
         ^^^^^^^^^^^"
     ;
@@ -392,34 +411,39 @@
 const y4 = y++;
            ^";
 static field self::AbstractClassWithConstructor abstractClassWithConstructor = invalid-expression "The class 'AbstractClassWithConstructor' is abstract and can't be instantiated.";
-static const field self::ExtendsFoo1 extendsFoo1 = invalid-expression "pkg/front_end/testcases/general/constants/various.dart:118:39: Error: Cannot invoke a non-'const' constructor where a const expression is expected.
+static const field self::ExtendsFoo1 extendsFoo1 = invalid-expression "pkg/front_end/testcases/general/constants/various.dart:128:39: Error: Cannot invoke a non-'const' constructor where a const expression is expected.
 Try using a constructor or factory that is 'const'.
 const ExtendsFoo1 extendsFoo1 = const ExtendsFoo1();
                                       ^^^^^^^^^^^";
-static const field self::ExtendsFoo2 extendsFoo2 = invalid-expression "pkg/front_end/testcases/general/constants/various.dart:121:9: Error: The superclass, 'Foo', has no unnamed constructor that takes no arguments.
+static const field self::ExtendsFoo2 extendsFoo2 = invalid-expression "pkg/front_end/testcases/general/constants/various.dart:131:9: Error: The superclass, 'Foo', has no unnamed constructor that takes no arguments.
   const ExtendsFoo2();
         ^^^^^^^^^^^";
 static const field self::Foo foo1 = #C13;
 static const field self::Foo foo2 = #C13;
 static const field core::bool foosIdentical = #C2;
 static const field core::bool foosEqual = #C2;
-static const field core::Symbol barFoo = #C14;
-static const field core::Symbol barFooEqual = #C15;
-static const field core::Symbol tripleShiftSymbol = #C16;
-static const field core::Symbol symbolWithDots = #C17;
+static const field self::FooWithHashCodeField fooWithHashCodeField1 = #C15;
+static const field self::FooWithHashCodeField fooWithHashCodeField2 = #C15;
+static const field core::bool fooWithHashCodeFieldIdentical = #C2;
+static const field core::bool fooWithHashCodeFieldEqual = invalid-expression "Binary operator '==' requires receiver constant 'FooWithHashCodeField {x: 42, y: 5, hashCode: 1764}' of a type with primitive equality or type 'double', but was of type 'FooWithHashCodeField'.
+ - 'FooWithHashCodeField' is from 'pkg/front_end/testcases/general/constants/various.dart'.";
+static const field core::Symbol barFoo = #C16;
+static const field core::Symbol barFooEqual = #C17;
+static const field core::Symbol tripleShiftSymbol = #C18;
+static const field core::Symbol symbolWithDots = #C19;
 static const field core::int circularity1 = invalid-expression "Constant expression depends on itself.";
 static const field core::int circularity2 = invalid-expression "Constant expression depends on itself.";
 static const field core::int circularity3 = invalid-expression "Constant expression depends on itself.";
 static const field core::int circularity4 = invalid-expression "Constant expression depends on itself.";
-static const field invalid-type function_const = invalid-expression "pkg/front_end/testcases/general/constants/various.dart:140:24: Error: Not a constant expression.
+static const field invalid-type function_const = invalid-expression "pkg/front_end/testcases/general/constants/various.dart:156:24: Error: Not a constant expression.
 const function_const = () {};
                        ^^";
 static field () → Null function_var = () → Null {};
 static field self::ConstClassWithFailingAssertWithEmptyMessage failedAssertEmptyMessage = invalid-expression "This assertion failed with message: (empty)";
-static const field self::ClassWithTypeArguments<dynamic, dynamic, dynamic> classWithTypeArguments1 = #C18;
-static const field self::ClassWithTypeArguments<dynamic, dynamic, dynamic> classWithTypeArguments2 = #C19;
+static const field self::ClassWithTypeArguments<dynamic, dynamic, dynamic> classWithTypeArguments1 = #C20;
+static const field self::ClassWithTypeArguments<dynamic, dynamic, dynamic> classWithTypeArguments2 = #C21;
 static const field core::bool classWithTypeArgumentsIdentical = #C1;
-static field self::ClassWithNonEmptyConstConstructor classWithNonEmptyConstConstructor = invalid-expression "pkg/front_end/testcases/general/constants/various.dart:168:7: Error: Cannot invoke a non-'const' constructor where a const expression is expected.
+static field self::ClassWithNonEmptyConstConstructor classWithNonEmptyConstConstructor = invalid-expression "pkg/front_end/testcases/general/constants/various.dart:184:7: Error: Cannot invoke a non-'const' constructor where a const expression is expected.
 Try using a constructor or factory that is 'const'.
 const ClassWithNonEmptyConstConstructor();
       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^";
@@ -432,16 +456,16 @@
 static const field core::bool zeroPointZeroEqualToZero = #C2;
 static const field core::bool zeroEqualToZeroPointZero = #C2;
 static const field core::bool nanEqual = #C1;
-static const field dynamic willBecomeNull = #C20;
+static const field dynamic willBecomeNull = #C22;
 static const field (core::int) → core::int willBecomeNullToo = invalid-expression "Expected constant 'null' to be of type 'int Function(int)', but was of type 'Null'.";
-static const field (core::int) → core::int partialInstantiation = #C22;
+static const field (core::int) → core::int partialInstantiation = #C24;
 static const field core::bool yBool = #C2;
 static const field core::bool zBool = #C1;
 static const field core::Object maybeInt = #C2;
 static const field core::bool isItInt = #C1;
 static const field core::Object maybeInt2 = #C2;
 static const field core::bool isItInt2 = #C1;
-static const field core::int? maybeInt3 = #C20;
+static const field core::int? maybeInt3 = #C22;
 static const field core::bool isItInt3 = #C1;
 static method id1<T extends core::Object? = dynamic>(self::id1::T% t) → self::id1::T%
   return t;
@@ -466,27 +490,31 @@
   #C11 = 1
   #C12 = 5
   #C13 = self::Foo {x:#C6, y:#C12}
-  #C14 = #Foo
-  #C15 = #Foo=
-  #C16 = #>>>
-  #C17 = #I.Have.Dots
-  #C18 = self::ClassWithTypeArguments<core::int, core::int, core::int> {}
-  #C19 = self::ClassWithTypeArguments<dynamic, dynamic, dynamic> {}
-  #C20 = null
-  #C21 = static-tearoff self::id1
-  #C22 = instantiation #C21 <core::int>
+  #C14 = 1764
+  #C15 = self::FooWithHashCodeField {x:#C6, y:#C12, hashCode:#C14}
+  #C16 = #Foo
+  #C17 = #Foo=
+  #C18 = #>>>
+  #C19 = #I.Have.Dots
+  #C20 = self::ClassWithTypeArguments<core::int, core::int, core::int> {}
+  #C21 = self::ClassWithTypeArguments<dynamic, dynamic, dynamic> {}
+  #C22 = null
+  #C23 = static-tearoff self::id1
+  #C24 = instantiation #C23 <core::int>
 }
 
 Extra constant evaluation status:
 Evaluated: InstanceGet @ org-dartlang-testcase:///various.dart:111:26 -> IntConstant(5)
-Extra constant evaluation: evaluated: 11, effectively constant: 1
+Evaluated: InstanceGet @ org-dartlang-testcase:///various.dart:120:26 -> IntConstant(5)
+Extra constant evaluation: evaluated: 15, effectively constant: 2
 
 
 Constructor coverage from constants:
 org-dartlang-testcase:///various.dart:
-- ExtendsFoo2. (from org-dartlang-testcase:///various.dart:121:9)
+- ExtendsFoo2. (from org-dartlang-testcase:///various.dart:131:9)
 - Foo. (from org-dartlang-testcase:///various.dart:109:9)
 - Object. (from org-dartlang-sdk:///sdk/lib/core/object.dart)
-- ConstClassWithFailingAssertWithEmptyMessage. (from org-dartlang-testcase:///various.dart:144:9)
-- ClassWithTypeArguments. (from org-dartlang-testcase:///various.dart:151:9)
-- ConstClassWithFinalFields2. (from org-dartlang-testcase:///various.dart:177:9)
+- FooWithHashCodeField. (from org-dartlang-testcase:///various.dart:118:9)
+- ConstClassWithFailingAssertWithEmptyMessage. (from org-dartlang-testcase:///various.dart:160:9)
+- ClassWithTypeArguments. (from org-dartlang-testcase:///various.dart:167:9)
+- ConstClassWithFinalFields2. (from org-dartlang-testcase:///various.dart:193:9)
diff --git a/pkg/front_end/testcases/general/constants/various.dart.textual_outline.expect b/pkg/front_end/testcases/general/constants/various.dart.textual_outline.expect
index 3d4ccd8..8cdaa8a 100644
--- a/pkg/front_end/testcases/general/constants/various.dart.textual_outline.expect
+++ b/pkg/front_end/testcases/general/constants/various.dart.textual_outline.expect
@@ -138,6 +138,13 @@
   const Foo(int x) : this.x = x, this.y = "hello".length;
 }
 
+class FooWithHashCodeField {
+  final int x;
+  final int y;
+  final int hashCode;
+  const FooWithHashCodeField(int x) : this.x = x, this.y = "hello".length, this.hashCode = x * 42;
+}
+
 class ExtendsFoo1 extends Foo {}
 
 const ExtendsFoo1 extendsFoo1 = const ExtendsFoo1();
@@ -156,6 +163,14 @@
 
 const bool foosEqual = foo1 == foo2;
 
+const FooWithHashCodeField fooWithHashCodeField1 = const FooWithHashCodeField(42);
+
+const FooWithHashCodeField fooWithHashCodeField2 = const FooWithHashCodeField(42);
+
+const bool fooWithHashCodeFieldIdentical = identical(fooWithHashCodeField1, fooWithHashCodeField2);
+
+const bool fooWithHashCodeFieldEqual = fooWithHashCodeField1 == fooWithHashCodeField2;
+
 const Symbol barFoo = const Symbol("Foo");
 
 const Symbol barFooEqual = const Symbol("Foo=");
diff --git a/pkg/front_end/testcases/records/record_named_constant_evaluation.dart b/pkg/front_end/testcases/records/record_named_constant_evaluation.dart
new file mode 100644
index 0000000..385afc5
--- /dev/null
+++ b/pkg/front_end/testcases/records/record_named_constant_evaluation.dart
@@ -0,0 +1,24 @@
+// Copyright (c) 2024, 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.
+
+// Mostly copied from issue54491.dart.
+
+void main() {
+  const Chk((Ex(1), foo: Ex(2)), eq: (1, foo: 2));
+  const Chk((1, foo: 2), eq: (1, foo: 2));
+  const Chk((Ex(1), foo: Ex(2)), eq: (Ex(1), foo: Ex(2)));
+  const Chk((Ex(1), foo: Ex(2)), eq: (1, foo: 2));
+  const Chk(((1 as Ex), foo: (2 as Ex)), eq: (1, foo: 2));
+  const Chk((Ex(1) as int, foo: Ex(2) as int), eq: (1, foo: 2));
+  const Chk((Ex(1), foo: Ex(2)) as (int, {int foo}), eq: (1, foo: 2));
+  const Chk((1, foo: 2) as (Ex, {int foo}), eq: (1, foo: 2));
+  const Chk(Ex((1, foo: 2)), eq: (1, foo: 2));
+  const Chk(Ex((Ex(1), foo: Ex(2))), eq: (1, foo: 2));
+}
+class Chk {
+  const Chk(Object? v, {required Object? eq}) :
+    assert(v == eq, "Not equal ${(v, eq: eq)}");
+}
+
+extension type const Ex(Object? value) {}
diff --git a/pkg/front_end/testcases/records/record_named_constant_evaluation.dart.strong.expect b/pkg/front_end/testcases/records/record_named_constant_evaluation.dart.strong.expect
new file mode 100644
index 0000000..1a68754
--- /dev/null
+++ b/pkg/front_end/testcases/records/record_named_constant_evaluation.dart.strong.expect
@@ -0,0 +1,42 @@
+library;
+import self as self;
+import "dart:core" as core;
+
+class Chk extends core::Object /*hasConstConstructor*/  {
+  const constructor •(core::Object? v, {required core::Object? eq}) → self::Chk
+    : assert(v =={core::Object::==}{(core::Object) → core::bool} eq, "Not equal ${(v, {eq: eq})}"), super core::Object::•()
+    ;
+}
+extension type Ex(core::Object? value) {
+  abstract extension-type-member representation-field get value() → core::Object?;
+  constructor • = self::Ex|constructor#;
+  constructor tearoff • = self::Ex|constructor#_#new#tearOff;
+}
+static method main() → void {
+  #C1;
+  #C1;
+  #C1;
+  #C1;
+  #C1;
+  #C1;
+  #C1;
+  #C1;
+  #C1;
+  #C1;
+}
+static extension-type-member method Ex|constructor#(core::Object? value) → self::Ex% /* erasure=core::Object?, declared=! */ {
+  lowered final self::Ex% /* erasure=core::Object?, declared=! */ #this = value;
+  return #this;
+}
+static extension-type-member method Ex|constructor#_#new#tearOff(core::Object? value) → self::Ex% /* erasure=core::Object?, declared=! */
+  return self::Ex|constructor#(value);
+
+constants  {
+  #C1 = self::Chk {}
+}
+
+
+Constructor coverage from constants:
+org-dartlang-testcase:///record_named_constant_evaluation.dart:
+- Chk. (from org-dartlang-testcase:///record_named_constant_evaluation.dart:20:9)
+- Object. (from org-dartlang-sdk:///sdk/lib/core/object.dart)
diff --git a/pkg/front_end/testcases/records/record_named_constant_evaluation.dart.strong.modular.expect b/pkg/front_end/testcases/records/record_named_constant_evaluation.dart.strong.modular.expect
new file mode 100644
index 0000000..1a68754
--- /dev/null
+++ b/pkg/front_end/testcases/records/record_named_constant_evaluation.dart.strong.modular.expect
@@ -0,0 +1,42 @@
+library;
+import self as self;
+import "dart:core" as core;
+
+class Chk extends core::Object /*hasConstConstructor*/  {
+  const constructor •(core::Object? v, {required core::Object? eq}) → self::Chk
+    : assert(v =={core::Object::==}{(core::Object) → core::bool} eq, "Not equal ${(v, {eq: eq})}"), super core::Object::•()
+    ;
+}
+extension type Ex(core::Object? value) {
+  abstract extension-type-member representation-field get value() → core::Object?;
+  constructor • = self::Ex|constructor#;
+  constructor tearoff • = self::Ex|constructor#_#new#tearOff;
+}
+static method main() → void {
+  #C1;
+  #C1;
+  #C1;
+  #C1;
+  #C1;
+  #C1;
+  #C1;
+  #C1;
+  #C1;
+  #C1;
+}
+static extension-type-member method Ex|constructor#(core::Object? value) → self::Ex% /* erasure=core::Object?, declared=! */ {
+  lowered final self::Ex% /* erasure=core::Object?, declared=! */ #this = value;
+  return #this;
+}
+static extension-type-member method Ex|constructor#_#new#tearOff(core::Object? value) → self::Ex% /* erasure=core::Object?, declared=! */
+  return self::Ex|constructor#(value);
+
+constants  {
+  #C1 = self::Chk {}
+}
+
+
+Constructor coverage from constants:
+org-dartlang-testcase:///record_named_constant_evaluation.dart:
+- Chk. (from org-dartlang-testcase:///record_named_constant_evaluation.dart:20:9)
+- Object. (from org-dartlang-sdk:///sdk/lib/core/object.dart)
diff --git a/pkg/front_end/testcases/records/record_named_constant_evaluation.dart.strong.outline.expect b/pkg/front_end/testcases/records/record_named_constant_evaluation.dart.strong.outline.expect
new file mode 100644
index 0000000..798d8be
--- /dev/null
+++ b/pkg/front_end/testcases/records/record_named_constant_evaluation.dart.strong.outline.expect
@@ -0,0 +1,22 @@
+library;
+import self as self;
+import "dart:core" as core;
+
+class Chk extends core::Object /*hasConstConstructor*/  {
+  const constructor •(core::Object? v, {required core::Object? eq}) → self::Chk
+    : assert(v =={core::Object::==}{(core::Object) → core::bool} eq, "Not equal ${(v, {eq: eq})}"), super core::Object::•()
+    ;
+}
+extension type Ex(core::Object? value) {
+  abstract extension-type-member representation-field get value() → core::Object?;
+  constructor • = self::Ex|constructor#;
+  constructor tearoff • = self::Ex|constructor#_#new#tearOff;
+}
+static method main() → void
+  ;
+static extension-type-member method Ex|constructor#(core::Object? value) → self::Ex% /* erasure=core::Object?, declared=! */ {
+  lowered final self::Ex% /* erasure=core::Object?, declared=! */ #this = value;
+  return #this;
+}
+static extension-type-member method Ex|constructor#_#new#tearOff(core::Object? value) → self::Ex% /* erasure=core::Object?, declared=! */
+  return self::Ex|constructor#(value);
diff --git a/pkg/front_end/testcases/records/record_named_constant_evaluation.dart.strong.transformed.expect b/pkg/front_end/testcases/records/record_named_constant_evaluation.dart.strong.transformed.expect
new file mode 100644
index 0000000..1a68754
--- /dev/null
+++ b/pkg/front_end/testcases/records/record_named_constant_evaluation.dart.strong.transformed.expect
@@ -0,0 +1,42 @@
+library;
+import self as self;
+import "dart:core" as core;
+
+class Chk extends core::Object /*hasConstConstructor*/  {
+  const constructor •(core::Object? v, {required core::Object? eq}) → self::Chk
+    : assert(v =={core::Object::==}{(core::Object) → core::bool} eq, "Not equal ${(v, {eq: eq})}"), super core::Object::•()
+    ;
+}
+extension type Ex(core::Object? value) {
+  abstract extension-type-member representation-field get value() → core::Object?;
+  constructor • = self::Ex|constructor#;
+  constructor tearoff • = self::Ex|constructor#_#new#tearOff;
+}
+static method main() → void {
+  #C1;
+  #C1;
+  #C1;
+  #C1;
+  #C1;
+  #C1;
+  #C1;
+  #C1;
+  #C1;
+  #C1;
+}
+static extension-type-member method Ex|constructor#(core::Object? value) → self::Ex% /* erasure=core::Object?, declared=! */ {
+  lowered final self::Ex% /* erasure=core::Object?, declared=! */ #this = value;
+  return #this;
+}
+static extension-type-member method Ex|constructor#_#new#tearOff(core::Object? value) → self::Ex% /* erasure=core::Object?, declared=! */
+  return self::Ex|constructor#(value);
+
+constants  {
+  #C1 = self::Chk {}
+}
+
+
+Constructor coverage from constants:
+org-dartlang-testcase:///record_named_constant_evaluation.dart:
+- Chk. (from org-dartlang-testcase:///record_named_constant_evaluation.dart:20:9)
+- Object. (from org-dartlang-sdk:///sdk/lib/core/object.dart)
diff --git a/pkg/front_end/testcases/records/record_named_constant_evaluation.dart.textual_outline.expect b/pkg/front_end/testcases/records/record_named_constant_evaluation.dart.textual_outline.expect
new file mode 100644
index 0000000..3cebb70
--- /dev/null
+++ b/pkg/front_end/testcases/records/record_named_constant_evaluation.dart.textual_outline.expect
@@ -0,0 +1,8 @@
+void main() {}
+
+class Chk {
+  const Chk(Object? v, {required Object? eq})
+      : assert(v == eq, "Not equal ${(v, eq: eq)}");
+}
+
+extension type const Ex(Object? value) {}
diff --git a/pkg/front_end/testcases/records/record_named_constant_evaluation.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/records/record_named_constant_evaluation.dart.textual_outline_modelled.expect
new file mode 100644
index 0000000..35c1721
--- /dev/null
+++ b/pkg/front_end/testcases/records/record_named_constant_evaluation.dart.textual_outline_modelled.expect
@@ -0,0 +1,8 @@
+class Chk {
+  const Chk(Object? v, {required Object? eq})
+      : assert(v == eq, "Not equal ${(v, eq: eq)}");
+}
+
+extension type const Ex(Object? value) {}
+
+void main() {}
diff --git a/pkg/front_end/tool/coverage_merger.dart b/pkg/front_end/tool/coverage_merger.dart
index ab39df9..e2679c4 100644
--- a/pkg/front_end/tool/coverage_merger.dart
+++ b/pkg/front_end/tool/coverage_merger.dart
@@ -6,7 +6,7 @@
 import 'dart:typed_data';
 
 import 'package:_fe_analyzer_shared/src/scanner/characters.dart'
-    show $SPACE, $CARET;
+    show $SPACE, $CARET, $LF, $CR;
 import 'package:_fe_analyzer_shared/src/scanner/token.dart';
 import 'package:front_end/src/util/parser_ast.dart';
 import 'package:front_end/src/util/parser_ast_helper.dart';
@@ -16,20 +16,29 @@
 import '../test/coverage_helper.dart';
 import 'interval_list.dart';
 import 'parser_ast_indexer.dart';
+import 'utils.dart';
 
 void main(List<String> arguments) {
   Uri? coverageUri;
   Uri? packagesUri;
+  bool addCommentsToFiles = false;
+  bool removeCommentsFromFiles = false;
 
   for (String argument in arguments) {
     const String coverage = "--coverage=";
     const String packages = "--packages=";
+    const String comment = "--comment";
+    const String removeComments = "--remove-comments";
     if (argument.startsWith(coverage)) {
       coverageUri =
           Uri.base.resolveUri(Uri.file(argument.substring(coverage.length)));
     } else if (argument.startsWith(packages)) {
       packagesUri =
           Uri.base.resolveUri(Uri.file(argument.substring(packages.length)));
+    } else if (argument == comment) {
+      addCommentsToFiles = true;
+    } else if (argument == removeComments) {
+      removeCommentsFromFiles = true;
     } else {
       throw "Unsupported argument: $argument";
     }
@@ -42,7 +51,15 @@
   }
 
   Stopwatch stopwatch = new Stopwatch()..start();
-  mergeFromDirUri(packagesUri, coverageUri, silent: false);
+  mergeFromDirUri(
+    packagesUri,
+    coverageUri,
+    silent: false,
+    extraCoverageIgnores: ["coverage-ignore(suite):"],
+    extraCoverageBlockIgnores: ["coverage-ignore-block(suite):"],
+    addCommentsToFiles: addCommentsToFiles,
+    removeCommentsFromFiles: removeCommentsFromFiles,
+  );
   print("Done in ${stopwatch.elapsed}");
 }
 
@@ -50,6 +67,10 @@
   Uri packagesUri,
   Uri coverageUri, {
   required bool silent,
+  required List<String> extraCoverageIgnores,
+  required List<String> extraCoverageBlockIgnores,
+  bool addCommentsToFiles = false,
+  bool removeCommentsFromFiles = false,
 }) {
   void output(Object? object) {
     if (silent) return;
@@ -116,16 +137,34 @@
   for (Uri uri in knownUris.toList()
     ..sort(((a, b) => a.toString().compareTo(b.toString())))) {
     // Don't care about coverage for testing stuff.
-    if (uri.toString().startsWith("package:front_end/src/testing/")) continue;
+    String uriString = uri.toString();
+    if (uriString.startsWith("package:front_end/src/testing/")) continue;
+    if (uriString == "package:front_end/src/util/parser_ast_helper.dart" ||
+        uriString ==
+            "package:front_end/src/api_prototype/experimental_flags_generated.dart" ||
+        uriString == "package:front_end/src/codes/cfe_codes_generated.dart") {
+      continue;
+    }
 
     Hit? hit = hits[uri];
     Set<int>? miss = misses[uri];
     List<int> hitsSorted =
         hit == null ? const [] : (hit._data.keys.toList()..sort());
 
-    CoverageInfo processInfo =
-        process(packageConfig, uri, miss ?? const {}, hitsSorted);
-    output(processInfo.visualization);
+    CoverageInfo processInfo = process(
+      packageConfig,
+      uri,
+      miss ?? const {},
+      hitsSorted,
+      extraCoverageIgnores,
+      extraCoverageBlockIgnores,
+      addCommentsToFiles: addCommentsToFiles,
+      removeCommentsFromFiles: removeCommentsFromFiles,
+    );
+    if (processInfo.visualization.trim().isNotEmpty) {
+      output(processInfo.visualization);
+      output("");
+    }
     result[uri] = processInfo;
     filesCount++;
     if (processInfo.error) {
@@ -137,8 +176,6 @@
       hitsTotal += processInfo.hitCount;
       missesTotal += processInfo.missCount;
     }
-
-    output("");
   }
 
   output("Processed $filesCount files with $errorsCount error(s) and "
@@ -170,8 +207,16 @@
       : error = false;
 }
 
-CoverageInfo process(PackageConfig packageConfig, Uri uri,
-    Set<int> untrimmedMisses, List<int> hitsSorted) {
+CoverageInfo process(
+  PackageConfig packageConfig,
+  Uri uri,
+  Set<int> untrimmedMisses,
+  List<int> hitsSorted,
+  List<String> extraCoverageIgnores,
+  List<String> extraCoverageBlockIgnores, {
+  bool addCommentsToFiles = false,
+  bool removeCommentsFromFiles = false,
+}) {
   Uri? fileUri = packageConfig.resolve(uri);
   if (fileUri == null) {
     return new CoverageInfo.error("Couldn't find file uri for $uri");
@@ -193,11 +238,200 @@
     enableExtensionMethods: true,
     enableNonNullable: true,
     enableTripleShift: true,
+    allowPatterns: true,
     lineStarts: lineStarts,
   );
 
+  Source source = new Source(lineStarts, rawBytes, uri, fileUri);
+
+  if (removeCommentsFromFiles) {
+    CompilationUnitBegin unitBegin =
+        ast.children!.first as CompilationUnitBegin;
+    Token? token = unitBegin.token;
+    List<Token> removeComments = [];
+    while (token != null && !token.isEof) {
+      Token? comment = token.precedingComments;
+      while (comment != null) {
+        String message = comment.lexeme.trim().toLowerCase();
+        while (message.startsWith("//") || message.startsWith("/*")) {
+          message = message.substring(2).trim();
+        }
+        for (String coverageIgnoreString in const [
+          "coverage-ignore(suite): not run.",
+          "coverage-ignore-block(suite): not run.",
+        ]) {
+          if (message.startsWith(coverageIgnoreString)) {
+            removeComments.add(comment);
+          }
+        }
+        comment = comment.next;
+      }
+      token = token.next;
+    }
+    String sourceText = source.text;
+    StringBuffer sb = new StringBuffer();
+    int from = 0;
+    for (Token token in removeComments) {
+      String substring = sourceText.substring(from, token.charOffset);
+      sb.write(substring);
+      from = token.charEnd;
+      // Remove whitespace after too.
+      while (sourceText.length > from &&
+          (sourceText.codeUnitAt(from) == $SPACE ||
+              sourceText.codeUnitAt(from) == $LF ||
+              sourceText.codeUnitAt(from) == $CR)) {
+        from++;
+      }
+    }
+    sb.write(sourceText.substring(from));
+    f.writeAsStringSync(sb.toString());
+    print("Removed ${removeComments.length} in $uri");
+
+    // Return a fake result.
+    return new CoverageInfo(
+        allCovered: true, missCount: -1, hitCount: -1, visualization: "fake 2");
+  }
+
+  List<int> allSorted = [...hitsSorted, ...untrimmedMisses]..sort();
   AstIndexerAndIgnoreCollector astIndexer =
-      AstIndexerAndIgnoreCollector.collect(ast);
+      AstIndexerAndIgnoreCollector.collect(
+          ast, extraCoverageIgnores, extraCoverageBlockIgnores,
+          hitsSorted: hitsSorted, allSorted: allSorted);
+
+  IntervalList ignoredIntervals =
+      astIndexer.ignoredStartEnd.buildIntervalList();
+
+  if (addCommentsToFiles) {
+    String sourceText = source.text;
+    StringBuffer sb = new StringBuffer();
+    int from = 0;
+    astIndexer.potentiallyAddCommentTokens.sort();
+    List<_CommentOn> processed = [];
+    for (_CommentOn commentOn in astIndexer.potentiallyAddCommentTokens) {
+      bool doAdd = true;
+      if (processed.isNotEmpty) {
+        _CommentOn prevAdded = processed.last;
+
+        if (prevAdded.beginToken.charOffset <=
+                commentOn.beginToken.charOffset &&
+            prevAdded.endToken.charEnd >= commentOn.endToken.charEnd) {
+          // The previous added "block" contain this one.
+          doAdd = false;
+
+          if (commentOn.commentOnToken.lexeme == "." ||
+              commentOn.commentOnToken.lexeme == "?.") {
+            // A comment on the actual call isn't pretty.
+            // Allow the "bigger one".
+          } else if (prevAdded.canBeReplaced) {
+            // Though if there aren't any possible extra coverage in the
+            // previous block compared to this one, we do prefer the smaller
+            // one.
+            int allSortedIndex =
+                binarySearch(allSorted, commentOn.beginToken.charOffset);
+            if (allSortedIndex < allSorted.length &&
+                allSorted[allSortedIndex] < commentOn.beginToken.charOffset) {
+              allSortedIndex++;
+            }
+            if (allSortedIndex > 0 &&
+                allSorted[allSortedIndex - 1] <
+                    prevAdded.beginToken.charOffset) {
+              // The block before this can't have any coverage.
+              // Now find the first point outside this range.
+              int i = binarySearch(allSorted, commentOn.endToken.charEnd) + 1;
+              if (i < allSorted.length &&
+                  allSorted[i] > prevAdded.endToken.charEnd) {
+                // The previous one doesn't have any possible coverage points
+                // that this one doesn't. We prefer the smaller one and will
+                // therefore replace it.
+                processed.removeLast();
+                doAdd = true;
+              }
+            }
+          }
+        }
+      }
+      if (doAdd) {
+        processed.add(commentOn);
+      }
+    }
+    for (_CommentOn entry in processed) {
+      // If - on a file without ignore comments - an ignore comment is
+      // pushed down (say inside an if instead of outside it), on a subsequent
+      // run, because now that ignore inside the if is already present there's
+      // nothing to push down and the one outside the if will be added.
+      // We don't want that, so verify that a new comment will actually ignore
+      // possible coverage points that wasn't covered/ignored before.
+      int sortedIndex = binarySearch(allSorted, entry.beginToken.charOffset);
+      if (sortedIndex < allSorted.length &&
+          allSorted[sortedIndex] < entry.beginToken.charOffset) {
+        sortedIndex++;
+      }
+      bool doAdd = false;
+      while (sortedIndex < allSorted.length &&
+          allSorted[sortedIndex] <= entry.endToken.charEnd) {
+        if (!ignoredIntervals.contains(allSorted[sortedIndex])) {
+          doAdd = true;
+          break;
+        }
+        sortedIndex++;
+      }
+
+      if (!doAdd) {
+        continue;
+      }
+
+      Token token = entry.commentOnToken;
+      String extra = "";
+      if (token.previous?.lexeme == "&&" ||
+          token.previous?.lexeme == "||" ||
+          token.previous?.lexeme == "(" ||
+          token.previous?.lexeme == ")" ||
+          token.previous?.lexeme == "}" ||
+          token.previous?.lexeme == "?" ||
+          token.previous?.lexeme == ":" ||
+          token.previous?.lexeme == ";" ||
+          token.previous?.lexeme == "=" ||
+          token.lexeme == "?.") {
+        extra = "\n  ";
+        // If adding an extra linebreak would introduce an empty line we won't
+        // add it.
+        for (int i = token.charOffset - 1; i >= token.previous!.charEnd; i--) {
+          int codeUnit = sourceText.codeUnitAt(i);
+          if (codeUnit == $SPACE) {
+            // We ignore spaces.
+          } else if (codeUnit == $LF || codeUnit == $CR) {
+            // Found linebreak: Adding a linebreak would add an empty line.
+            extra = "";
+            break;
+          } else {
+            // We found a non-space before a linebreak.
+            // Let's just add the linebreak.
+            break;
+          }
+        }
+      }
+      if (token.precedingComments != null) {
+        token = token.precedingComments!;
+      }
+      String substring = sourceText.substring(from, token.charOffset);
+      sb.write(substring);
+
+      // The extra spaces at the end makes the formatter format better if for
+      // instance there's comments after this.
+      if (entry.isBlock) {
+        sb.write("$extra// Coverage-ignore-block(suite): Not run.\n  ");
+      } else {
+        sb.write("$extra// Coverage-ignore(suite): Not run.\n  ");
+      }
+      from = token.charOffset;
+    }
+    sb.write(sourceText.substring(from));
+    f.writeAsStringSync(sb.toString());
+
+    // Return a fake result.
+    return new CoverageInfo(
+        allCovered: true, missCount: -1, hitCount: -1, visualization: "fake");
+  }
 
   // TODO(jensj): Extract all comments and use those as well here.
   // TODO(jensj): Should some comment throw/report and error if covered?
@@ -205,8 +439,6 @@
 
   StringBuffer visualization = new StringBuffer();
 
-  IntervalList ignoredIntervals =
-      astIndexer.ignoredStartEnd.buildIntervalList();
   var (:bool allCovered, :Set<int> trimmedMisses) =
       _trimIgnoredAndPrintPercentages(
           visualization, ignoredIntervals, untrimmedMisses, hitsSorted, uri);
@@ -221,7 +453,6 @@
 
   CompilationUnitBegin unitBegin = ast.children!.first as CompilationUnitBegin;
   Token firstToken = unitBegin.token;
-  Source source = new Source(lineStarts, rawBytes, uri, fileUri);
 
   List<int> sortedMisses = trimmedMisses.toList()..sort();
 
@@ -285,8 +516,8 @@
         String? name = astIndexer.nameOfEntitySpanning(offset);
 
         if (name != null) {
-          visualization.writeln(
-              "$uri:${location.line}: No coverage for '$name'.\n$line\n");
+          visualization.writeln("$uri:${location.line}: "
+              "No coverage for '$name' ($offset).\n$line\n");
           // TODO(jensj): Squiggly line under the identifier of the entity?
         } else {
           visualization.writeln(
@@ -371,7 +602,7 @@
           "($missCount misses)");
       return (allCovered: false, trimmedMisses: trimmedMisses);
     } else {
-      visualization.writeln("$uri: 100% (OK)");
+      // visualization.writeln("$uri: 100% (OK)");
       return (allCovered: true, trimmedMisses: trimmedMisses);
     }
   }
@@ -423,15 +654,32 @@
     "debugName",
     "writeNullabilityOn",
   };
+  final List<String> _coverageIgnores = [
+    "coverage-ignore:",
+  ];
+  final List<String> _coverageBlockIgnores = [
+    "coverage-ignore-block:",
+  ];
 
   final IntervalListBuilder ignoredStartEnd = new IntervalListBuilder();
 
+  final List<int> hitsSorted;
+  int hitsSortedIndex = 0;
+  final List<int> allSorted;
+  int allSortedIndex = 0;
+
+  final List<_CommentOn> potentiallyAddCommentTokens = [];
+
   late final _AstIndexerAndIgnoreCollectorBody _collectorBody =
       new _AstIndexerAndIgnoreCollectorBody(this);
 
-  static AstIndexerAndIgnoreCollector collect(ParserAstNode ast) {
+  static AstIndexerAndIgnoreCollector collect(ParserAstNode ast,
+      List<String> extraCoverageIgnores, List<String> extraCoverageBlockIgnores,
+      {required List<int> hitsSorted, required List<int> allSorted}) {
     AstIndexerAndIgnoreCollector collector =
-        new AstIndexerAndIgnoreCollector._();
+        new AstIndexerAndIgnoreCollector._(hitsSorted, allSorted);
+    collector._coverageIgnores.addAll(extraCoverageIgnores);
+    collector._coverageBlockIgnores.addAll(extraCoverageBlockIgnores);
     ast.accept(collector);
 
     assert(collector.positionNodeIndex.length ==
@@ -442,10 +690,228 @@
     return collector;
   }
 
-  AstIndexerAndIgnoreCollector._() {}
+  AstIndexerAndIgnoreCollector._(this.hitsSorted, this.allSorted) {}
+
+  bool _hasIgnoreComment(Token tokenWithPossibleComment,
+      {required bool isBlock}) {
+    List<String> coverageIgnores = _coverageIgnores;
+    if (isBlock) {
+      coverageIgnores = _coverageBlockIgnores;
+    }
+    Token? comment = tokenWithPossibleComment.precedingComments;
+    while (comment != null) {
+      String message = comment.lexeme.trim().toLowerCase();
+      while (message.startsWith("//") || message.startsWith("/*")) {
+        message = message.substring(2).trim();
+      }
+      for (String coverageIgnoreString in coverageIgnores) {
+        if (message.startsWith(coverageIgnoreString)) {
+          return true;
+        }
+      }
+      comment = comment.next;
+    }
+    return false;
+  }
+
+  /// Check if there is an ignore comment on [tokenWithPossibleComment] and
+  /// returns true if there is.
+  ///
+  /// If there is not it will add a note to add one if that makes sense (in that
+  /// there is possible coverage but no actual coverage).
+  bool _checkCommentAndIgnoreCoverage(
+      Token tokenWithPossibleComment, BeginAndEndTokenParserAstNode ignoreRange,
+      {required bool allowReplace}) {
+    return _checkCommentAndIgnoreCoverageWithBeginAndEnd(
+        tokenWithPossibleComment, ignoreRange.beginToken, ignoreRange.endToken,
+        allowReplace: allowReplace);
+  }
+
+  /// Check if there is an ignore comment on [tokenWithPossibleComment] and
+  /// returns true if there is.
+  ///
+  /// If there is not it will add a note to add one if that makes sense (in that
+  /// there is possible coverage but no actual coverage).
+  bool _checkCommentAndIgnoreCoverageWithBeginAndEnd(
+      Token tokenWithPossibleComment, Token beginToken, Token endToken,
+      {required bool allowReplace,
+      bool isBlock = false,
+      bool allowOnBraceStart = false}) {
+    if (_hasIgnoreComment(tokenWithPossibleComment, isBlock: isBlock)) {
+      ignoredStartEnd.addIntervalIncludingEnd(
+          beginToken.charOffset, endToken.charEnd);
+      return true;
+    }
+
+    // Should a comment be added here?
+    if (tokenWithPossibleComment.lexeme == "{" && !allowOnBraceStart) {
+      // We don't want to add it "outside" the block, but inside it,
+      // so we just return here.
+      return false;
+    }
+    if (tokenWithPossibleComment.lexeme == "else" &&
+        tokenWithPossibleComment.next!.lexeme == "{") {
+      // An else with a block, prefer it directly inside the block instead.
+      return false;
+    }
+    // Because of (at least) `visitEndingBinaryExpressionHandle` we can get
+    // events out of order. Go back here if needed...
+    if (allSorted.isNotEmpty) {
+      if (allSorted.length >= allSortedIndex) {
+        allSortedIndex = allSorted.length - 1;
+      }
+      while (allSortedIndex > 0 &&
+          allSorted[allSortedIndex] > beginToken.charOffset) {
+        allSortedIndex--;
+      }
+      // ...then go forward if needed (e.g. when it does come in order).
+      while (allSortedIndex < allSorted.length &&
+          allSorted[allSortedIndex] < beginToken.charOffset) {
+        allSortedIndex++;
+      }
+    }
+
+    if (allSortedIndex >= allSorted.length ||
+        allSorted[allSortedIndex] > endToken.charEnd) {
+      // Nothing inside this block can be covered by the VM anyway.
+      return false;
+    }
+
+    // As before: Make work when events arrive out of order.
+    if (hitsSorted.isNotEmpty) {
+      if (hitsSorted.length >= hitsSortedIndex) {
+        hitsSortedIndex = hitsSorted.length - 1;
+      }
+      while (hitsSortedIndex > 0 &&
+          hitsSorted[hitsSortedIndex] > beginToken.charOffset) {
+        hitsSortedIndex--;
+      }
+      while (hitsSortedIndex < hitsSorted.length &&
+          hitsSorted[hitsSortedIndex] < beginToken.charOffset) {
+        hitsSortedIndex++;
+      }
+    }
+
+    if (hitsSortedIndex >= hitsSorted.length ||
+        hitsSorted[hitsSortedIndex] > endToken.charEnd) {
+      // No hits at all or next hit is after this "block".
+      potentiallyAddCommentTokens.add(new _CommentOn(
+        commentOnToken: tokenWithPossibleComment,
+        beginToken: beginToken,
+        endToken: endToken,
+        canBeReplaced: allowReplace,
+        isBlock: isBlock,
+      ));
+    }
+
+    return false;
+  }
+
+  /// Check if there is an ignore comment on [tokenWithPossibleComment] and
+  /// returns true if there is.
+  ///
+  /// If there is not it will add a note to add one if that makes sense (in that
+  /// there is possible coverage but no actual coverage).
+  ///
+  /// This method in particular will try to ignore from
+  /// [tokenWithPossibleComment] until the end of the block that it's inside,
+  /// but fall back to the original range if that's not possible.
+  /// Two different comments are used to distinguish these cases.
+  bool _checkCommentAndIgnoreCoverageUntilEndOfBlockOrEnd(
+      Token tokenWithPossibleComment,
+      Token beginToken,
+      ParserAstNode node,
+      Token endToken,
+      {required bool allowReplace}) {
+    ParserAstNode? parent = node.parent;
+    if ((parent is BlockFunctionBodyEnd || parent is BlockEnd)) {
+      if (_checkCommentAndIgnoreCoverageWithBeginAndEnd(
+          tokenWithPossibleComment,
+          beginToken,
+          (parent as BeginAndEndTokenParserAstNode).endToken,
+          allowReplace: allowReplace,
+          isBlock: true)) {
+        return true;
+      }
+    }
+    return _checkCommentAndIgnoreCoverageWithBeginAndEnd(
+        tokenWithPossibleComment, beginToken, endToken,
+        allowReplace: allowReplace);
+  }
+
+  bool _ignoreIfChildrenIsThrow(BeginAndEndTokenParserAstNode node) {
+    List<ParserAstNode>? children = node.children;
+    if (children == null) return false;
+    if (children.length >= 4 &&
+        children[1] is NewExpressionEnd &&
+        children[2] is ThrowExpressionHandle &&
+        children[3] is ExpressionStatementHandle) {
+      ignoredStartEnd.addIntervalIncludingEnd(
+          node.beginToken.charOffset, node.endToken.charEnd);
+      return true;
+    }
+    return false;
+  }
+
+  @override
+  void visitClassDeclarationEnd(ClassDeclarationEnd node) {
+    // Note that this stops recursing meaning there'll be stuff we can't look
+    // up. If that turns out to be a problem we can likely just not return,
+    // possible "double-ignored" coverages should still work fine because of the
+    // interval list.
+    if (_checkCommentAndIgnoreCoverage(node.beginToken, node,
+        allowReplace: false)) return;
+    super.visitClassDeclarationEnd(node);
+  }
+
+  @override
+  void visitExtensionDeclarationEnd(ExtensionDeclarationEnd node) {
+    // Note that this stops recursing meaning there'll be stuff we can't look
+    // up. If that turns out to be a problem we can likely just not return,
+    // possible "double-ignored" coverages should still work fine because of the
+    // interval list.
+    if (_checkCommentAndIgnoreCoverage(node.beginToken, node,
+        allowReplace: false)) return;
+    super.visitExtensionDeclarationEnd(node);
+  }
+
+  @override
+  void visitExtensionTypeDeclarationEnd(ExtensionTypeDeclarationEnd node) {
+    // Note that this stops recursing meaning there'll be stuff we can't look
+    // up. If that turns out to be a problem we can likely just not return,
+    // possible "double-ignored" coverages should still work fine because of the
+    // interval list.
+    if (_checkCommentAndIgnoreCoverage(node.beginToken, node,
+        allowReplace: false)) return;
+    super.visitExtensionTypeDeclarationEnd(node);
+  }
+
+  @override
+  void visitTopLevelFieldsEnd(TopLevelFieldsEnd node) {
+    super.visitTopLevelFieldsEnd(node);
+    assert(positionNodeIndex.last == node);
+    assert(positionStartEndIndex.last == node.endToken.charEnd);
+    int index = positionNodeIndex.length - 1;
+    int firstIndex = moveNodeIndexToFirstMetadataIfAny(index)!;
+    Token beginToken = node.beginToken;
+    if (firstIndex < index) {
+      MetadataEnd metadata = positionNodeIndex[firstIndex] as MetadataEnd;
+      beginToken = metadata.beginToken;
+    }
+
+    if (_checkCommentAndIgnoreCoverageWithBeginAndEnd(
+        node.beginToken, beginToken, node.endToken,
+        allowReplace: false)) {
+      // Ignore these class fields including metadata.
+      ignoredStartEnd.addIntervalIncludingEnd(
+          positionStartEndIndex[firstIndex * 2 + 0], node.endToken.charEnd);
+    }
+  }
 
   @override
   void visitTopLevelMethodEnd(TopLevelMethodEnd node) {
+    if (_checkCommentAndIgnoreCoverage(node.beginToken, node,
+        allowReplace: false)) return;
     super.visitTopLevelMethodEnd(node);
     String name = node.getNameIdentifier().token.lexeme;
     if (topLevelMethodNamesToIgnore.contains(name)) {
@@ -461,15 +927,63 @@
     }
   }
 
+  /// This method will try to recognize entities (think methods) that just
+  /// throws. If it finds [node] to be this, it will add it to the ignore list
+  /// and return true.
+  bool _ignoreIfEntityWithThrowBody(BeginAndEndTokenParserAstNode node) {
+    List<ParserAstNode>? children = node.children;
+    if (children == null) return false;
+    for (ParserAstNode child in children) {
+      if (child is BlockFunctionBodyEnd && _ignoreIfChildrenIsThrow(child)) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  @override
+  void containerFields(
+      BeginAndEndTokenParserAstNode node, List<IdentifierHandle> names) {
+    super.containerFields(node, names);
+    assert(positionNodeIndex.last == node);
+    assert(positionStartEndIndex.last == node.endToken.charEnd);
+    int index = positionNodeIndex.length - 1;
+    int firstIndex = moveNodeIndexToFirstMetadataIfAny(index)!;
+    Token beginToken = node.beginToken;
+    if (firstIndex < index) {
+      MetadataEnd metadata = positionNodeIndex[firstIndex] as MetadataEnd;
+      beginToken = metadata.beginToken;
+    }
+
+    if (_checkCommentAndIgnoreCoverageWithBeginAndEnd(
+        node.beginToken, beginToken, node.endToken,
+        allowReplace: false)) {
+      // Ignore these class fields including metadata.
+      ignoredStartEnd.addIntervalIncludingEnd(
+          positionStartEndIndex[firstIndex * 2 + 0], node.endToken.charEnd);
+    }
+  }
+
   @override
   void containerMethod(BeginAndEndTokenParserAstNode node, String name) {
     super.containerMethod(node, name);
-    if (classMethodNamesToIgnore.contains(name)) {
+    assert(positionNodeIndex.last == node);
+    assert(positionStartEndIndex.last == node.endToken.charEnd);
+    int index = positionNodeIndex.length - 1;
+    int firstIndex = moveNodeIndexToFirstMetadataIfAny(index)!;
+    Token beginToken = node.beginToken;
+    if (firstIndex < index) {
+      MetadataEnd metadata = positionNodeIndex[firstIndex] as MetadataEnd;
+      beginToken = metadata.beginToken;
+    }
+
+    if (_ignoreIfEntityWithThrowBody(node) ||
+        classMethodNamesToIgnore.contains(name) ||
+        _checkCommentAndIgnoreCoverageWithBeginAndEnd(
+            node.beginToken, beginToken, node.endToken,
+            allowReplace: false)) {
       // Ignore this class method including metadata.
-      assert(positionNodeIndex.last == node);
-      assert(positionStartEndIndex.last == node.endToken.charEnd);
-      int index = positionNodeIndex.length - 1;
-      int firstIndex = moveNodeIndexToFirstMetadataIfAny(index)!;
+
       ignoredStartEnd.addIntervalIncludingEnd(
           positionStartEndIndex[firstIndex * 2 + 0], node.endToken.charEnd);
     } else {
@@ -506,18 +1020,340 @@
         return true;
       }
     }
+    if (_collector._ignoreIfChildrenIsThrow(node)) return true;
     return false;
   }
 
   @override
   void visitReturnStatementEnd(ReturnStatementEnd node) {
     if (_recordIfIsCallToNotExpectedCoverage(node)) return;
+    if (_collector._checkCommentAndIgnoreCoverage(node.beginToken, node,
+        allowReplace: false)) {
+      return;
+    }
     super.visitReturnStatementEnd(node);
   }
 
   @override
   void visitBlockEnd(BlockEnd node) {
     if (_recordIfIsCallToNotExpectedCoverage(node)) return;
+    if (_collector._checkCommentAndIgnoreCoverageWithBeginAndEnd(
+        node.beginToken.next!, node.beginToken, node.endToken,
+        allowReplace: false, isBlock: true)) {
+      return;
+    }
     super.visitBlockEnd(node);
   }
+
+  @override
+  void visitBlockFunctionBodyEnd(BlockFunctionBodyEnd node) {
+    if (_recordIfIsCallToNotExpectedCoverage(node)) return;
+    if (_collector._checkCommentAndIgnoreCoverageWithBeginAndEnd(
+        node.beginToken.next!, node.beginToken, node.endToken,
+        allowReplace: false, isBlock: true)) {
+      return;
+    }
+    super.visitBlockFunctionBodyEnd(node);
+  }
+
+  @override
+  void visitFunctionExpressionEnd(FunctionExpressionEnd node) {
+    if (_collector._checkCommentAndIgnoreCoverage(node.beginToken, node,
+        allowReplace: true)) {
+      return;
+    }
+    super.visitFunctionExpressionEnd(node);
+  }
+
+  @override
+  void visitLocalFunctionDeclarationEnd(LocalFunctionDeclarationEnd node) {
+    ParserAstNode? beginNode = node.children?.firstOrNull;
+    if (beginNode is LocalFunctionDeclarationBegin) {
+      if (_collector._checkCommentAndIgnoreCoverageWithBeginAndEnd(
+          beginNode.token, beginNode.token, node.endToken,
+          allowReplace: true)) {
+        return;
+      }
+    }
+    super.visitLocalFunctionDeclarationEnd(node);
+  }
+
+  @override
+  void visitSwitchCaseEnd(SwitchCaseEnd node) {
+    if (_collector._checkCommentAndIgnoreCoverage(node.beginToken, node,
+        allowReplace: false)) {
+      return;
+    }
+    super.visitSwitchCaseEnd(node);
+  }
+
+  @override
+  void visitCaseExpressionEnd(CaseExpressionEnd node) {
+    if (_collector._checkCommentAndIgnoreCoverageWithBeginAndEnd(
+        node.caseKeyword, node.caseKeyword, node.colon,
+        allowReplace: false)) {
+      return;
+    }
+    super.visitCaseExpressionEnd(node);
+  }
+
+  @override
+  void visitThrowExpressionHandle(ThrowExpressionHandle node) {
+    if (_collector._checkCommentAndIgnoreCoverageUntilEndOfBlockOrEnd(
+        node.throwToken, node.throwToken, node, node.endToken,
+        allowReplace: true)) {
+      return;
+    }
+    super.visitThrowExpressionHandle(node);
+  }
+
+  @override
+  void visitElseStatementEnd(ElseStatementEnd node) {
+    if (_collector._checkCommentAndIgnoreCoverage(node.beginToken, node,
+        allowReplace: false)) {
+      return;
+    }
+    super.visitElseStatementEnd(node);
+  }
+
+  @override
+  void visitThenStatementEnd(ThenStatementEnd node) {
+    if (_collector._checkCommentAndIgnoreCoverage(node.beginToken, node,
+        allowReplace: true)) {
+      return;
+    }
+    super.visitThenStatementEnd(node);
+  }
+
+  @override
+  void visitIfStatementEnd(IfStatementEnd node) {
+    if (_collector._checkCommentAndIgnoreCoverageUntilEndOfBlockOrEnd(
+        node.ifToken, node.ifToken, node, node.endToken,
+        allowReplace: true)) {
+      return;
+    }
+    super.visitIfStatementEnd(node);
+  }
+
+  @override
+  void visitTryStatementEnd(TryStatementEnd node) {
+    if (_collector._checkCommentAndIgnoreCoverageUntilEndOfBlockOrEnd(
+        node.tryKeyword, node.tryKeyword, node, node.endToken,
+        allowReplace: true)) {
+      return;
+    }
+    super.visitTryStatementEnd(node);
+  }
+
+  @override
+  void visitBinaryExpressionEnd(BinaryExpressionEnd node) {
+    if (_collector._checkCommentAndIgnoreCoverageWithBeginAndEnd(
+        node.token.next!, node.token, node.endToken,
+        allowReplace: false)) {
+      return;
+    }
+    super.visitBinaryExpressionEnd(node);
+  }
+
+  @override
+  void visitEndingBinaryExpressionHandle(EndingBinaryExpressionHandle node) {
+    // Given `a?.b` if `a` is null `b` won't execute.
+    // Having the comment before the `?.` formats prettier.
+    if (_collector._checkCommentAndIgnoreCoverageWithBeginAndEnd(
+        node.token, node.token, node.endToken,
+        allowReplace: true)) {
+      return;
+    }
+    super.visitEndingBinaryExpressionHandle(node);
+  }
+
+  @override
+  void visitThenControlFlowHandle(ThenControlFlowHandle node) {
+    ParserAstNode? parent = node.parent;
+    if (parent is IfControlFlowEnd) {
+      if (_collector._checkCommentAndIgnoreCoverageWithBeginAndEnd(
+          node.token.next!, node.token.next!, parent.token,
+          allowReplace: false)) {
+        return;
+      }
+    }
+    super.visitThenControlFlowHandle(node);
+  }
+
+  @override
+  void visitConditionalExpressionEnd(ConditionalExpressionEnd node) {
+    // Visiting `foo ? bar : baz`:
+
+    // Check the comment on the `bar` part (i.e. between ? and :).
+    _collector._checkCommentAndIgnoreCoverageWithBeginAndEnd(
+        node.question.next!, node.question.next!, node.colon,
+        allowReplace: false);
+
+    // Check the comment on the `baz` part (i.e. between : and end).
+    _collector._checkCommentAndIgnoreCoverageWithBeginAndEnd(
+        node.colon.next!, node.colon.next!, node.endToken,
+        allowReplace: false);
+
+    super.visitConditionalExpressionEnd(node);
+  }
+
+  @override
+  void visitForLoopPartsHandle(ForLoopPartsHandle node) {
+    // Given `for(a; b; c)` --- the `c` part could be uncovered.
+    _collector._checkCommentAndIgnoreCoverageWithBeginAndEnd(
+        node.rightSeparator.next!,
+        node.rightSeparator.next!,
+        node.leftParen.endGroup!,
+        allowReplace: false);
+    super.visitForLoopPartsHandle(node);
+  }
+
+  @override
+  void visitForInBodyEnd(ForInBodyEnd node) {
+    ParserAstNode? beginNode = node.children?.firstOrNull;
+    if (beginNode is ForInBodyBegin) {
+      if (_collector._checkCommentAndIgnoreCoverageWithBeginAndEnd(
+          beginNode.token, beginNode.token, node.endToken,
+          allowReplace: true, allowOnBraceStart: true)) {
+        return;
+      }
+    }
+    super.visitForInBodyEnd(node);
+  }
+
+  @override
+  void visitExpressionStatementHandle(ExpressionStatementHandle node) {
+    // TODO(jensj): allowReplace should depend upon if there's anything
+    // coverable in this statement. If there isn't it should certainly be
+    // replaceable.
+    if (_collector._checkCommentAndIgnoreCoverageUntilEndOfBlockOrEnd(
+        node.beginToken, node.beginToken, node, node.endToken,
+        allowReplace: false)) {
+      return;
+    }
+    super.visitExpressionStatementHandle(node);
+  }
+
+  @override
+  void visitVariablesDeclarationEnd(VariablesDeclarationEnd node) {
+    // TODO(jensj): allowReplace should depend upon if there's anything
+    // coverable in this statement. If there isn't it should certainly be
+    // replaceable.
+    if (node.endToken != null) {
+      List<ParserAstNode> parentChildren = node.parent!.children!;
+      int thisIndex = parentChildren.indexOf(node);
+      if (thisIndex - 1 >= 0 && parentChildren[thisIndex - 1] is TypeHandle) {
+        TypeHandle type = parentChildren[thisIndex - 1] as TypeHandle;
+        if (_collector._checkCommentAndIgnoreCoverageUntilEndOfBlockOrEnd(
+            type.beginToken, type.beginToken, node, node.endToken!,
+            allowReplace: false)) {
+          return;
+        }
+      }
+    }
+    super.visitVariablesDeclarationEnd(node);
+  }
+
+  @override
+  void visitAssertEnd(AssertEnd node) {
+    if (_collector._checkCommentAndIgnoreCoverageUntilEndOfBlockOrEnd(
+        node.assertKeyword, node.assertKeyword, node, node.endToken,
+        allowReplace: true)) {
+      return;
+    }
+
+    if (node.commaToken != null) {
+      _collector._checkCommentAndIgnoreCoverageWithBeginAndEnd(
+          node.commaToken!.next!, node.commaToken!, node.endToken,
+          allowReplace: false);
+    }
+    super.visitAssertEnd(node);
+  }
+
+  @override
+  void visitCatchBlockHandle(CatchBlockHandle node) {
+    // TODO(jensj): allowReplace should depend upon if there's anything
+    // coverable in this statement. If there isn't it should certainly be
+    // replaceable.
+    if (node.onKeyword != null) {
+      List<ParserAstNode> parentChildren = node.parent!.children!;
+      int thisIndex = parentChildren.indexOf(node);
+      if (thisIndex - 1 >= 0 && parentChildren[thisIndex - 1] is BlockEnd) {
+        BlockEnd block = parentChildren[thisIndex - 1] as BlockEnd;
+        if (_collector._checkCommentAndIgnoreCoverageWithBeginAndEnd(
+            node.onKeyword!, node.onKeyword!, block.endToken,
+            allowReplace: false)) {
+          return;
+        }
+      }
+    }
+    super.visitCatchBlockHandle(node);
+  }
+
+  @override
+  void visitPatternEnd(PatternEnd node) {
+    ParserAstNode? beginNode = node.children?.firstOrNull;
+    if (beginNode is PatternBegin) {
+      Token begin = beginNode.token;
+      if (begin.lexeme != ":") {
+        begin = begin.next!;
+      }
+      if (_collector._checkCommentAndIgnoreCoverageWithBeginAndEnd(
+          begin, begin, node.token,
+          allowReplace: true)) {
+        return;
+      }
+    }
+    super.visitPatternEnd(node);
+  }
+
+  @override
+  void visitSwitchExpressionCaseEnd(SwitchExpressionCaseEnd node) {
+    // The entire thing?
+    if (_collector._checkCommentAndIgnoreCoverageWithBeginAndEnd(
+        node.beginToken, node.beginToken, node.endToken,
+        allowReplace: true)) {
+      return;
+    }
+
+    // The if-matched part?
+    _collector._checkCommentAndIgnoreCoverageWithBeginAndEnd(
+        node.arrow.next!, node.arrow.next!, node.endToken,
+        allowReplace: true);
+
+    super.visitSwitchExpressionCaseEnd(node);
+  }
+
+  @override
+  void visitAssignmentExpressionHandle(AssignmentExpressionHandle node) {
+    _collector._checkCommentAndIgnoreCoverageWithBeginAndEnd(
+        node.token.next!, node.token.next!, node.endToken,
+        allowReplace: true, allowOnBraceStart: true);
+    super.visitAssignmentExpressionHandle(node);
+  }
+}
+
+class _CommentOn implements Comparable<_CommentOn> {
+  final Token commentOnToken;
+  final Token beginToken;
+  final Token endToken;
+  final bool canBeReplaced;
+  final bool isBlock;
+
+  _CommentOn({
+    required this.commentOnToken,
+    required this.beginToken,
+    required this.endToken,
+    required this.canBeReplaced,
+    required this.isBlock,
+  });
+
+  @override
+  int compareTo(_CommentOn other) {
+    // Small to big.
+    int result = beginToken.charOffset.compareTo(other.beginToken.charOffset);
+    if (result != 0) return result;
+    // Big to small.
+    return other.endToken.charOffset.compareTo(endToken.charOffset);
+  }
 }
diff --git a/pkg/front_end/tool/utils.dart b/pkg/front_end/tool/utils.dart
new file mode 100644
index 0000000..8539667
--- /dev/null
+++ b/pkg/front_end/tool/utils.dart
@@ -0,0 +1,25 @@
+// Copyright (c) 2024, 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.
+
+/// Returns the index in the [list] at which [find] is or should have been.
+///
+/// [list] has to be sorted.
+/// If there are several elements matching what we're looking for it can return
+/// the index to of an arbitrary one of them.
+/// If there is no match the element at the index will be smaller than the data
+/// searched for.
+/// If the input list is empty it will return 0 which is not a valid entry.
+int binarySearch(List<int> list, int find) {
+  int low = 0, high = list.length - 1;
+  while (low < high) {
+    int mid = high - ((high - low) >> 1); // Get middle, rounding up.
+    int pivot = list[mid];
+    if (pivot <= find) {
+      low = mid;
+    } else {
+      high = mid - 1;
+    }
+  }
+  return low;
+}