Version 3.6.0-109.0.dev

Merge 786742f34cb4a55e6cfbc349a0918c97b54aa43a into dev
diff --git a/pkg/dart2wasm/lib/async.dart b/pkg/dart2wasm/lib/async.dart
index 55e4020..6d6d000 100644
--- a/pkg/dart2wasm/lib/async.dart
+++ b/pkg/dart2wasm/lib/async.dart
@@ -10,9 +10,7 @@
 import 'code_generator.dart';
 import 'state_machine.dart';
 
-class AsyncCodeGenerator extends StateMachineEntryCodeGenerator {
-  AsyncCodeGenerator(super.translator, super.function, super.reference);
-
+mixin AsyncCodeGeneratorMixin on StateMachineEntryAstCodeGenerator {
   late final ClassInfo asyncSuspendStateInfo =
       translator.classInfo[translator.asyncSuspendStateClass]!;
 
@@ -76,9 +74,9 @@
     b.return_();
     b.end();
 
-    AsyncStateMachineCodeGenerator(translator, resumeFun, reference,
+    AsyncStateMachineCodeGenerator(translator, resumeFun, enclosingMember,
             functionNode, functionSource, closures)
-        .generate();
+        .generate(resumeFun.locals.toList(), null);
   }
 
   w.FunctionBuilder _defineInnerBodyFunction(FunctionNode functionNode) =>
@@ -98,11 +96,26 @@
           "${function.functionName} inner");
 }
 
+/// Generates code for async procedures.
+class AsyncProcedureCodeGenerator
+    extends ProcedureStateMachineEntryCodeGenerator
+    with AsyncCodeGeneratorMixin {
+  AsyncProcedureCodeGenerator(
+      super.translator, super.function, super.enclosingMember);
+}
+
+/// Generates code for async closures.
+class AsyncLambdaCodeGenerator extends LambdaStateMachineEntryCodeGenerator
+    with AsyncCodeGeneratorMixin {
+  AsyncLambdaCodeGenerator(
+      super.translator, super.enclosingMember, super.lambda, super.closures);
+}
+
 class AsyncStateMachineCodeGenerator extends StateMachineCodeGenerator {
   AsyncStateMachineCodeGenerator(
       super.translator,
       super.function,
-      super.reference,
+      super.enclosingMember,
       super.functionNode,
       super.functionSource,
       super.closures);
diff --git a/pkg/dart2wasm/lib/code_generator.dart b/pkg/dart2wasm/lib/code_generator.dart
index aa3bf24..b8c65b6 100644
--- a/pkg/dart2wasm/lib/code_generator.dart
+++ b/pkg/dart2wasm/lib/code_generator.dart
@@ -21,6 +21,22 @@
 import 'translator.dart';
 import 'types.dart';
 
+abstract class CodeGenerator {
+  // The two parameters here are used for inlining:
+  //
+  // If the user
+  //
+  //   * inlines the code, it will provide locals and a return label
+  //
+  //   * doesn't inline (i.e. makes new function with this code) it will provide
+  //     the parameters of the function and no return label.
+  //
+  void generate(List<w.Local> paramLocals, w.Label? returnLabel);
+
+  // TODO(kustermann): Remove this again.
+  Closures get closures;
+}
+
 /// Main code generator for member bodies.
 ///
 /// The [generate] method first collects all local functions and function
@@ -36,19 +52,24 @@
 /// code generation for every expression or subexpression is done via the [wrap]
 /// method, which emits appropriate conversion code if the produced type is not
 /// a subtype of the expected type.
-class CodeGenerator extends ExpressionVisitor1<w.ValueType, w.ValueType>
+abstract class AstCodeGenerator
+    extends ExpressionVisitor1<w.ValueType, w.ValueType>
     with ExpressionVisitor1DefaultMixin<w.ValueType, w.ValueType>
-    implements InitializerVisitor<void>, StatementVisitor<void> {
+    implements InitializerVisitor<void>, StatementVisitor<void>, CodeGenerator {
   final Translator translator;
   final w.FunctionType functionType;
   final w.InstructionsBuilder b;
-  final Reference reference;
+  final Member enclosingMember;
+
+  // To be initialized in `generate()`
   late final List<w.Local> paramLocals;
-  final w.Label? returnLabel;
+  late final w.Label? returnLabel;
 
-  late final Intrinsifier intrinsifier;
-  late final StaticTypeContext typeContext;
+  late final Intrinsifier intrinsifier = Intrinsifier(this);
+  late final StaticTypeContext typeContext =
+      StaticTypeContext(enclosingMember, translator.typeEnvironment);
 
+  @override
   late final Closures closures;
 
   bool exceptionLocationPrinted = false;
@@ -82,45 +103,11 @@
       {};
 
   /// Create a code generator for a member or one of its lambdas.
-  ///
-  /// The [paramLocals] and [returnLabel] parameters can be used to generate
-  /// code for an inlined function by specifying the locals containing the
-  /// parameters (instead of the function inputs) and the label to jump to on
-  /// return (instead of emitting a `return` instruction).
-  CodeGenerator(this.translator, this.functionType, this.b, this.reference,
-      {required this.paramLocals, this.returnLabel}) {
-    intrinsifier = Intrinsifier(this);
-    typeContext = StaticTypeContext(member, translator.typeEnvironment);
-  }
-
-  /// Factory constructor for instantiating a code generator appropriate for
-  /// generating code for the given function. This will either return a
-  /// [CodeGenerator] or a [SyncStarCodeGenerator].
-  factory CodeGenerator.forFunction(
-      Translator translator,
-      FunctionNode? functionNode,
-      w.FunctionBuilder function,
-      Reference reference) {
-    bool isSyncStar = functionNode?.asyncMarker == AsyncMarker.SyncStar &&
-        !reference.isTearOffReference;
-    bool isAsync = functionNode?.asyncMarker == AsyncMarker.Async &&
-        !reference.isTearOffReference;
-    bool isTypeChecker = reference.isTypeCheckerReference;
-
-    if (!isTypeChecker && isSyncStar) {
-      return SyncStarCodeGenerator(translator, function, reference);
-    } else if (!isTypeChecker && isAsync) {
-      return AsyncCodeGenerator(translator, function, reference);
-    } else {
-      return CodeGenerator(translator, function.type, function.body, reference,
-          paramLocals: function.locals.toList());
-    }
-  }
+  AstCodeGenerator(
+      this.translator, this.functionType, this.b, this.enclosingMember);
 
   w.ModuleBuilder get m => translator.m;
 
-  Member get member => reference.asMember;
-
   List<w.ValueType> get outputs => functionType.outputs;
 
   w.ValueType get returnType => translator.outputOrVoid(outputs);
@@ -206,8 +193,8 @@
     final location = source.getLocation(fileUri, fileOffset);
     final old = _sourceMapFileOffset;
     _sourceMapFileOffset = fileOffset;
-    b.startSourceMapping(
-        fileUri, location.line - 1, location.column - 1, member.name.text);
+    b.startSourceMapping(fileUri, location.line - 1, location.column - 1,
+        enclosingMember.name.text);
     return old;
   }
 
@@ -219,152 +206,18 @@
     return (oldSource, oldFileOffset);
   }
 
-  /// Generate code for the member.
-  void generate() {
-    Member member = this.member;
+  /// Generate code while preventing recursive inlining.
+  @override
+  void generate(List<w.Local> paramLocals, w.Label? returnLabel) {
+    this.paramLocals = paramLocals;
+    this.returnLabel = returnLabel;
 
-    if (member is Constructor) {
-      // Closures are built when constructor functions are added to worklist.
-      closures = translator.constructorClosures[member.reference]!;
-    } else {
-      // Build closure information.
-      closures = Closures(translator, this.member);
-    }
-
-    if (reference.isTearOffReference) {
-      return generateTearOffGetter(member as Procedure);
-    }
-
-    if (reference.isTypeCheckerReference) {
-      if (member is Field || (member is Procedure && member.isSetter)) {
-        return _generateFieldSetterTypeCheckerMethod();
-      } else {
-        return _generateProcedureTypeCheckerMethod();
-      }
-    }
-
-    if (intrinsifier.generateMemberIntrinsic(
-        reference, functionType, paramLocals, returnLabel)) {
-      b.end();
-      return;
-    }
-
-    if (member.isExternal) {
-      b.comment("Unimplemented external member $member at ${member.location}");
-      if (member.isInstanceMember) {
-        b.local_get(paramLocals[0]);
-      } else {
-        b.ref_null(w.HeapType.none);
-      }
-      translator.constants.instantiateConstant(
-          b,
-          SymbolConstant(member.name.text, null),
-          translator.classInfo[translator.symbolClass]!.nonNullableType);
-      call(translator
-          .noSuchMethodErrorThrowUnimplementedExternalMemberError.reference);
-      b.unreachable();
-      b.end();
-      return;
-    }
-
-    final source = member.enclosingComponent!.uriToSource[member.fileUri]!;
-    setSourceMapSourceAndFileOffset(source, member.fileOffset);
-
-    if (member is Constructor) {
-      translator.membersBeingGenerated.add(member);
-      if (reference.isConstructorBodyReference) {
-        generateConstructorBody(reference);
-      } else if (reference.isInitializerReference) {
-        generateInitializerList(reference);
-      } else {
-        generateConstructorAllocator(member);
-      }
-      translator.membersBeingGenerated.remove(member);
-      return;
-    }
-
-    if (member is Field) {
-      if (member.isStatic) {
-        return generateStaticFieldInitializer(member);
-      } else {
-        return generateImplicitAccessor(member);
-      }
-    }
-
-    assert(member.function!.asyncMarker != AsyncMarker.SyncStar);
-    assert(member.function!.asyncMarker != AsyncMarker.Async);
-
-    translator.membersBeingGenerated.add(member);
-    generateBody(member);
-    translator.membersBeingGenerated.remove(member);
+    translator.membersBeingGenerated.add(enclosingMember);
+    generateInternal();
+    translator.membersBeingGenerated.remove(enclosingMember);
   }
 
-  void generateTearOffGetter(Procedure procedure) {
-    _initializeThis(member.reference);
-    DartType functionType = translator.getTearOffType(procedure);
-    ClosureImplementation closure = translator.getTearOffClosure(procedure);
-    w.StructType struct = closure.representation.closureStruct;
-
-    ClassInfo info = translator.closureInfo;
-    translator.functions.recordClassAllocation(info.classId);
-
-    b.i32_const(info.classId);
-    b.i32_const(initialIdentityHash);
-    b.local_get(paramLocals[0]); // `this` as context
-    b.global_get(closure.vtable);
-    types.makeType(this, functionType);
-    b.struct_new(struct);
-    b.end();
-  }
-
-  void generateStaticFieldInitializer(Field field) {
-    // Static field initializer function
-    assert(reference == field.fieldReference);
-    closures.findCaptures(field);
-    closures.collectContexts(field);
-    closures.buildContexts();
-
-    w.Global global = translator.globals.getGlobal(field);
-    w.Global? flag = translator.globals.getGlobalInitializedFlag(field);
-    wrap(field.initializer!, global.type.type);
-    b.global_set(global);
-    if (flag != null) {
-      b.i32_const(1);
-      b.global_set(flag);
-    }
-    b.global_get(global);
-    translator.convertType(b, global.type.type, outputs.single);
-    b.end();
-  }
-
-  void generateImplicitAccessor(Field field) {
-    // Implicit getter or setter
-    w.StructType struct = translator.classInfo[field.enclosingClass!]!.struct;
-    int fieldIndex = translator.fieldIndex[field]!;
-    w.ValueType fieldType = struct.fields[fieldIndex].type.unpacked;
-
-    void getThis() {
-      w.Local thisLocal = paramLocals[0];
-      w.RefType structType = w.RefType.def(struct, nullable: false);
-      b.local_get(thisLocal);
-      translator.convertType(b, thisLocal.type, structType);
-    }
-
-    if (reference.isImplicitGetter) {
-      // Implicit getter
-      getThis();
-      b.struct_get(struct, fieldIndex);
-      translator.convertType(b, fieldType, returnType);
-    } else {
-      // Implicit setter
-      w.Local valueLocal = paramLocals[1];
-      getThis();
-      b.local_get(valueLocal);
-      translator.convertType(b, valueLocal.type, fieldType);
-      b.struct_set(struct, fieldIndex);
-    }
-    b.end();
-  }
+  void generateInternal();
 
   void _setupLocalParameters(Member member, ParameterInfo paramInfo,
       int parameterOffset, int implicitParams,
@@ -506,8 +359,8 @@
         isForwarder: isForwarder);
   }
 
-  void setupParametersAndContexts(Reference reference) {
-    setupParameters(reference);
+  void setupParametersAndContexts(Member member) {
+    setupParameters(member.reference);
 
     closures.findCaptures(member);
     closures.collectContexts(member);
@@ -517,37 +370,6 @@
     captureParameters();
   }
 
-  void setupInitializerListParametersAndContexts(Reference reference) {
-    setupParameters(reference, isForwarder: true);
-    allocateContext(member);
-    captureParameters();
-  }
-
-  void setupConstructorBodyParametersAndContexts(Reference reference) {
-    Constructor member = reference.asConstructor;
-    ParameterInfo paramInfo = translator.paramInfoForDirectCall(reference);
-
-    // For constructor body functions, the first parameter is always the
-    // receiver parameter, and the second parameter is a reference to the
-    // current context (if it exists).
-    Context? context = closures.contexts[member];
-    bool hasConstructorContext = context != null;
-
-    if (hasConstructorContext) {
-      assert(!context.isEmpty);
-      _initializeContextLocals(member, contextParamIndex: 1);
-    }
-
-    // Skips the receiver param (_initializeThis will return 1), and the
-    // context param if this exists.
-    int parameterOffset =
-        _initializeThis(reference) + (hasConstructorContext ? 1 : 0);
-    int implicitParams = parameterOffset + paramInfo.typeParamCount;
-
-    _setupLocalParameters(member, paramInfo, parameterOffset, implicitParams);
-    allocateContext(member.function);
-  }
-
   void _setupDefaultFieldValues(ClassInfo info) {
     fieldLocals.clear();
 
@@ -569,62 +391,6 @@
     }
   }
 
-  List<w.Local> _generateInitializers(Constructor member) {
-    Class cls = member.enclosingClass;
-    ClassInfo info = translator.classInfo[cls]!;
-    List<w.Local> superclassFields = [];
-
-    _setupDefaultFieldValues(info);
-
-    // Generate initializer list
-    for (Initializer initializer in member.initializers) {
-      visitInitializer(initializer);
-
-      if (initializer is SuperInitializer) {
-        // Save super classes' fields to locals
-        ClassInfo superInfo = info.superInfo!;
-
-        for (w.ValueType outputType
-            in superInfo.getClassFieldTypes().reversed) {
-          w.Local local = addLocal(outputType);
-          b.local_set(local);
-          superclassFields.add(local);
-        }
-      } else if (initializer is RedirectingInitializer) {
-        // Save redirected classes' fields to locals
-        List<w.Local> redirectedFields = [];
-
-        for (w.ValueType outputType in info.getClassFieldTypes().reversed) {
-          w.Local local = addLocal(outputType);
-          b.local_set(local);
-          redirectedFields.add(local);
-        }
-
-        return redirectedFields.reversed.toList();
-      }
-    }
-
-    List<w.Local> typeFields = [];
-
-    for (TypeParameter typeParam in cls.typeParameters) {
-      TypeParameter? match = info.typeParameterMatch[typeParam];
-
-      if (match == null) {
-        // Type is not contained in super class' fields
-        typeFields.add(typeLocals[typeParam]!);
-      }
-    }
-
-    List<w.Local> orderedFieldLocals = Map.fromEntries(
-            fieldLocals.entries.toList()
-              ..sort((x, y) => translator.fieldIndex[x.key]!
-                  .compareTo(translator.fieldIndex[y.key]!)))
-        .values
-        .toList();
-
-    return superclassFields.reversed.toList() + typeFields + orderedFieldLocals;
-  }
-
   List<w.Local> _getConstructorArgumentLocals(Reference target,
       [reverse = false]) {
     Constructor member = target.asConstructor;
@@ -661,113 +427,6 @@
     return constructorArgs;
   }
 
-  void generateBody(Member member) {
-    assert(member is! Constructor);
-    setupParametersAndContexts(member.reference);
-
-    Statement? body = member.function!.body;
-    if (body != null) {
-      visitStatement(body);
-    }
-
-    _implicitReturn();
-    b.end();
-  }
-
-  // Generates a function for allocating an object. This calls the separate
-  // initializer list and constructor body methods, and allocates a struct for
-  // the object.
-  void generateConstructorAllocator(Constructor member) {
-    setupParameters(member.reference, isForwarder: true);
-
-    w.FunctionType initializerMethodType =
-        translator.signatureForDirectCall(member.initializerReference);
-
-    List<w.Local> constructorArgs =
-        _getConstructorArgumentLocals(member.reference);
-
-    for (w.Local local in constructorArgs) {
-      b.local_get(local);
-    }
-
-    b.comment("Direct call of '$member Initializer'");
-    call(member.initializerReference);
-
-    ClassInfo info = translator.classInfo[member.enclosingClass]!;
-
-    // Add evaluated fields to locals
-    List<w.Local> orderedFieldLocals = [];
-
-    List<w.FieldType> fieldTypes = info.struct.fields
-        .sublist(FieldIndex.objectFieldBase)
-        .reversed
-        .toList();
-
-    for (w.FieldType field in fieldTypes) {
-      w.Local local = addLocal(field.type.unpacked);
-      orderedFieldLocals.add(local);
-      b.local_set(local);
-    }
-
-    Context? context = closures.contexts[member];
-    w.Local? contextLocal;
-
-    bool hasContext = context != null;
-
-    if (hasContext) {
-      assert(!context.isEmpty);
-      w.ValueType contextRef = w.RefType.struct(nullable: true);
-      contextLocal = addLocal(contextRef);
-      b.local_set(contextLocal);
-    }
-
-    List<w.ValueType> initializerOutputTypes = initializerMethodType.outputs;
-    int numConstructorBodyArgs = initializerOutputTypes.length -
-        fieldTypes.length -
-        (hasContext ? 1 : 0);
-
-    // Pop all arguments to constructor body
-    List<w.ValueType> constructorArgTypes =
-        initializerOutputTypes.sublist(0, numConstructorBodyArgs);
-
-    List<w.Local> constructorArguments = [];
-
-    for (w.ValueType argType in constructorArgTypes.reversed) {
-      w.Local local = addLocal(argType);
-      b.local_set(local);
-      constructorArguments.add(local);
-    }
-
-    // Set field values
-    b.i32_const(info.classId);
-    b.i32_const(initialIdentityHash);
-
-    for (w.Local local in orderedFieldLocals.reversed) {
-      b.local_get(local);
-    }
-
-    // create new struct with these fields and set to local
-    w.Local temp = addLocal(info.nonNullableType);
-    b.struct_new(info.struct);
-    b.local_tee(temp);
-
-    // Push context local if it is present
-    if (contextLocal != null) {
-      b.local_get(contextLocal);
-    }
-
-    // Push all constructor arguments
-    for (w.Local constructorArg in constructorArguments) {
-      b.local_get(constructorArg);
-    }
-
-    b.comment("Direct call of $member Constructor Body");
-    call(member.constructorBodyReference);
-
-    b.local_get(temp);
-    b.end();
-  }
-
   void setupLambdaParametersAndContexts(Lambda lambda) {
     FunctionNode functionNode = lambda.functionNode;
     _initializeContextLocals(functionNode);
@@ -787,22 +446,6 @@
     captureParameters();
   }
 
-  /// Generate code for the body of a lambda.
-  void generateLambda(Lambda lambda, Closures closures) {
-    // Initialize closure information from enclosing member.
-    this.closures = closures;
-
-    setSourceMapSource(lambda.functionNodeSource);
-
-    assert(lambda.functionNode.asyncMarker != AsyncMarker.Async);
-
-    setupLambdaParametersAndContexts(lambda);
-
-    visitStatement(lambda.functionNode.body!);
-    _implicitReturn();
-    b.end();
-  }
-
   /// Initialize locals containing `this` in constructors and instance members.
   /// Returns the number of parameter locals taken up by the receiver parameter,
   /// i.e. the parameter offset for the first type parameter (or the first
@@ -1005,9 +648,11 @@
   }
 
   List<w.ValueType> call(Reference target) {
-    if (translator.shouldInline(target)) {
-      w.FunctionType targetFunctionType =
-          translator.signatureForDirectCall(target);
+    w.FunctionType targetFunctionType =
+        translator.signatureForDirectCall(target);
+    final inliningCodeGen =
+        translator.getInliningCodeGenerator(target, targetFunctionType, b);
+    if (inliningCodeGen != null) {
       List<w.Local> inlinedLocals =
           targetFunctionType.inputs.map((t) => addLocal(t)).toList();
       for (w.Local local in inlinedLocals.reversed) {
@@ -1015,9 +660,7 @@
       }
       w.Label block = b.block(const [], targetFunctionType.outputs);
       b.comment("Inlined ${target.asMember}");
-      CodeGenerator(translator, targetFunctionType, b, target,
-              paramLocals: inlinedLocals, returnLabel: block)
-          .generate();
+      inliningCodeGen.generate(inlinedLocals, block);
       return targetFunctionType.outputs;
     } else {
       w.BaseFunction targetFunction = translator.functions.getFunction(target);
@@ -1879,7 +1522,7 @@
 
   Member _lookupSuperTarget(Member interfaceTarget, {required bool setter}) {
     return translator.hierarchy.getDispatchTarget(
-        member.enclosingClass!.superclass!, interfaceTarget.name,
+        enclosingMember.enclosingClass!.superclass!, interfaceTarget.name,
         setter: setter)!;
   }
 
@@ -3187,22 +2830,17 @@
   w.ValueType instantiateTypeParameter(TypeParameter parameter) {
     w.ValueType resultType;
 
-    // `this` will not be initialized yet for constructor initializer lists
-    if (parameter.declaration is GenericFunction ||
-        reference.isInitializerReference) {
-      // Type argument to function
-      w.Local? local = typeLocals[parameter];
-      if (local != null) {
-        b.local_get(local);
-        resultType = local.type;
-      } else {
-        Capture capture = closures.captures[parameter]!;
-        b.local_get(capture.context.currentLocal);
-        b.struct_get(capture.context.struct, capture.fieldIndex);
-        resultType = capture.type;
-      }
+    w.Local? local = typeLocals[parameter];
+    Capture? capture = closures.captures[parameter];
+    if (local != null) {
+      b.local_get(local);
+      resultType = local.type;
+    } else if (capture != null) {
+      Capture capture = closures.captures[parameter]!;
+      b.local_get(capture.context.currentLocal);
+      b.struct_get(capture.context.struct, capture.fieldIndex);
+      resultType = capture.type;
     } else {
-      // Type argument of class
       Class cls = parameter.declaration as Class;
       ClassInfo info = translator.classInfo[cls]!;
       int fieldIndex = translator.typeParameterIndex[parameter]!;
@@ -3210,6 +2848,7 @@
       b.struct_get(info.struct, fieldIndex);
       resultType = info.struct.fields[fieldIndex].type.unpacked;
     }
+
     translator.convertType(b, resultType, types.nonNullableTypeType);
     return types.nonNullableTypeType;
   }
@@ -3267,199 +2906,317 @@
     return wrap(node.expression, expectedType);
   }
 
-  // Generates a function for a constructor's body, where the allocated struct
-  // object is passed to this function.
-  void generateConstructorBody(Reference target) {
-    assert(target.isConstructorBodyReference);
-    Constructor member = target.asConstructor;
+  /// Generate code that checks type of an argument against an expected type
+  /// and throws a `TypeError` on failure.
+  ///
+  /// Does not expect any values on stack and does not leave any values on
+  /// stack.
+  ///
+  /// Locals [argLocal] are used to store values pushed by [pushArg]
+  /// and reuse the values.
+  ///
+  /// [argName] is used in the type error as the name of the argument that
+  /// doesn't match the expected type.
+  void _generateArgumentTypeCheck(
+    String argName,
+    void Function() pushArg,
+    DartType testedAgainstType,
+    w.Local argLocal,
+  ) {
+    if (translator.options.minify) {
+      // We don't need to include the name in the error message, so we can use
+      // the optimized `as` checks.
+      pushArg();
+      types.emitAsCheck(
+          this,
+          testedAgainstType,
+          translator.coreTypes.objectNullableRawType,
+          argLocal.type as w.RefType);
+      b.drop();
+    } else {
+      pushArg();
+      b.local_tee(argLocal);
+      types.emitIsTest(
+          this, testedAgainstType, translator.coreTypes.objectNullableRawType);
+      b.i32_eqz();
+      b.if_();
+      b.local_get(argLocal);
+      types.makeType(this, testedAgainstType);
+      _emitString(argName);
+      call(translator.stackTraceCurrent.reference);
+      call(translator.throwArgumentTypeCheckError.reference);
+      b.unreachable();
+      b.end();
+    }
+  }
 
-    setupConstructorBodyParametersAndContexts(target);
+  void _generateTypeArgumentBoundCheck(
+    String argName,
+    w.Local typeLocal,
+    DartType bound,
+  ) {
+    b.local_get(typeLocal);
+    final boundLocal =
+        b.addLocal(translator.classInfo[translator.typeClass]!.nonNullableType);
+    types.makeType(this, bound);
+    b.local_tee(boundLocal);
+    call(translator.isTypeSubtype.reference);
 
-    int getStartIndexForSuperOrRedirectedConstructorArguments() {
-      // Skips the receiver param and the current constructor's context
-      // (if it exists)
-      Context? context = closures.contexts[member];
-      bool hasContext = context != null;
+    b.i32_eqz();
+    b.if_();
+    // Type check failed
+    b.local_get(typeLocal);
+    b.local_get(boundLocal);
+    _emitString(argName);
+    call(translator.stackTraceCurrent.reference);
+    call(translator.throwTypeArgumentBoundCheckError.reference);
+    b.unreachable();
+    b.end();
+  }
 
-      if (hasContext) {
-        assert(!context.isEmpty);
-      }
+  void _emitString(String str) => wrap(StringLiteral(str),
+      translator.translateType(translator.coreTypes.stringNonNullableRawType));
 
-      int numSkippedParams = hasContext ? 2 : 1;
+  @override
+  void visitPatternSwitchStatement(PatternSwitchStatement node) {
+    // This node is internal to the front end and removed by the constant
+    // evaluator.
+    throw UnsupportedError("CodeGenerator.visitPatternSwitchStatement");
+  }
 
-      // Skips the current constructor's arguments
-      int numConstructorArgs = _getConstructorArgumentLocals(target).length;
+  @override
+  void visitPatternVariableDeclaration(PatternVariableDeclaration node) {
+    // This node is internal to the front end and removed by the constant
+    // evaluator.
+    throw UnsupportedError("CodeGenerator.visitPatternVariableDeclaration");
+  }
 
-      return numSkippedParams + numConstructorArgs;
+  @override
+  void visitIfCaseStatement(IfCaseStatement node) {
+    // This node is internal to the front end and removed by the constant
+    // evaluator.
+    throw UnsupportedError("CodeGenerator.visitIfCaseStatement");
+  }
+
+  void debugRuntimePrint(String s) {
+    final printFunction =
+        translator.functions.getFunction(translator.printToConsole.reference);
+    translator.constants.instantiateConstant(
+        b, StringConstant(s), printFunction.type.inputs[0]);
+    b.call(printFunction);
+  }
+
+  @override
+  void visitAuxiliaryStatement(AuxiliaryStatement node) {
+    throw UnsupportedError(
+        "Unsupported auxiliary statement $node (${node.runtimeType}).");
+  }
+
+  @override
+  void visitAuxiliaryInitializer(AuxiliaryInitializer node) {
+    throw UnsupportedError(
+        "Unsupported auxiliary initializer $node (${node.runtimeType}).");
+  }
+
+  void emitUnimplementedExternalError(Member member) {
+    b.comment("Unimplemented external member $member at ${member.location}");
+    if (member.isInstanceMember) {
+      b.local_get(paramLocals[0]);
+    } else {
+      b.ref_null(w.HeapType.none);
+    }
+    translator.constants.instantiateConstant(
+        b,
+        SymbolConstant(member.name.text, null),
+        translator.classInfo[translator.symbolClass]!.nonNullableType);
+    call(translator
+        .noSuchMethodErrorThrowUnimplementedExternalMemberError.reference);
+    b.unreachable();
+  }
+}
+
+CodeGenerator getMemberCodeGenerator(Translator translator,
+    w.FunctionBuilder functionBuilder, Reference memberReference) {
+  final member = memberReference.asMember;
+  final asyncMarker = member.function?.asyncMarker ?? AsyncMarker.Sync;
+  final codeGen = getInlinableMemberCodeGenerator(translator, asyncMarker,
+      functionBuilder.type, functionBuilder.body, memberReference);
+  if (codeGen != null) return codeGen;
+
+  final procedure = member as Procedure;
+
+  if (asyncMarker == AsyncMarker.SyncStar) {
+    return SyncStarProcedureCodeGenerator(
+        translator, functionBuilder, procedure);
+  }
+  assert(asyncMarker == AsyncMarker.Async);
+  return AsyncProcedureCodeGenerator(translator, functionBuilder, procedure);
+}
+
+CodeGenerator getLambdaCodeGenerator(Translator translator, Lambda lambda,
+    Member enclosingMember, Closures enclosingMemberClosures) {
+  final asyncMarker = lambda.functionNode.asyncMarker;
+
+  if (asyncMarker == AsyncMarker.Async) {
+    return AsyncLambdaCodeGenerator(
+        translator, enclosingMember, lambda, enclosingMemberClosures);
+  }
+  if (asyncMarker == AsyncMarker.SyncStar) {
+    return SyncStarLambdaCodeGenerator(
+        translator, enclosingMember, lambda, enclosingMemberClosures);
+  }
+  assert(asyncMarker == AsyncMarker.Sync);
+  return SynchronousLambdaCodeGenerator(
+      translator, enclosingMember, lambda, enclosingMemberClosures);
+}
+
+/// Returns a [CodeGenerator] for the given member iff that member can be
+/// inlined.
+CodeGenerator? getInlinableMemberCodeGenerator(
+    Translator translator,
+    AsyncMarker asyncMarker,
+    w.FunctionType functionType,
+    w.InstructionsBuilder b,
+    Reference reference) {
+  final Member member = reference.asMember;
+
+  if (reference.isTearOffReference) {
+    return TearOffCodeGenerator(translator, functionType, b, member);
+  }
+  if (reference.isTypeCheckerReference) {
+    return TypeCheckerCodeGenerator(translator, functionType, b, member);
+  }
+
+  if (member is Constructor) {
+    if (reference.isConstructorBodyReference) {
+      return ConstructorCodeGenerator(translator, functionType, b, member);
+    } else if (reference.isInitializerReference) {
+      return InitializerListCodeGenerator(translator, functionType, b, member);
+    } else {
+      return ConstructorAllocatorCodeGenerator(
+          translator, functionType, b, member);
+    }
+  }
+
+  if (member is Field) {
+    if (member.isStatic) {
+      return StaticFieldInitializerCodeGenerator(
+          translator, functionType, b, member);
+    }
+    return ImplicitFieldAccessorCodeGenerator(
+        translator, functionType, b, member, reference.isImplicitGetter);
+  }
+
+  if (member is Procedure && asyncMarker == AsyncMarker.Sync) {
+    return SynchronousProcedureCodeGenerator(
+        translator, functionType, b, member);
+  }
+  assert(
+      asyncMarker == AsyncMarker.SyncStar || asyncMarker == AsyncMarker.Async);
+  return null;
+}
+
+class SynchronousProcedureCodeGenerator extends AstCodeGenerator {
+  final Procedure member;
+
+  SynchronousProcedureCodeGenerator(Translator translator,
+      w.FunctionType functionType, w.InstructionsBuilder b, this.member)
+      : super(translator, functionType, b, member);
+
+  @override
+  void generateInternal() {
+    final source = member.enclosingComponent!.uriToSource[member.fileUri]!;
+    setSourceMapSourceAndFileOffset(source, member.fileOffset);
+
+    // Build closure information.
+    closures = Closures(translator, member);
+
+    if (intrinsifier.generateMemberIntrinsic(
+        member.reference, functionType, paramLocals, returnLabel)) {
+      b.end();
+      return;
     }
 
-    // Call super class' constructor body, or redirected constructor
-    for (Initializer initializer in member.initializers) {
-      if (initializer is SuperInitializer ||
-          initializer is RedirectingInitializer) {
-        Constructor target = initializer is SuperInitializer
-            ? initializer.target
-            : (initializer as RedirectingInitializer).target;
-
-        Supertype? supersupertype = target.enclosingClass.supertype;
-
-        if (supersupertype == null) {
-          break;
-        }
-
-        int startIndex =
-            getStartIndexForSuperOrRedirectedConstructorArguments();
-
-        List<w.Local> superOrRedirectedConstructorArgs =
-            paramLocals.sublist(startIndex);
-
-        w.Local object = thisLocal!;
-        b.local_get(object);
-
-        for (w.Local local in superOrRedirectedConstructorArgs) {
-          b.local_get(local);
-        }
-
-        call(target.constructorBodyReference);
-        break;
-      }
+    if (member.isExternal) {
+      emitUnimplementedExternalError(member);
+      b.end();
+      return;
     }
 
+    setupParametersAndContexts(member);
+
     Statement? body = member.function.body;
-
     if (body != null) {
       visitStatement(body);
     }
 
+    _implicitReturn();
     b.end();
   }
+}
 
-  // Generates a constructor's initializer list method, and returns:
-  // 1. Arguments and contexts returned from a super or redirecting initializer
-  //    method (in reverse order).
-  // 2. Arguments for this constructor (in reverse order).
-  // 3. A reference to the context for this constructor (or null if there is no
-  //    context).
-  // 4. Class fields (including superclass fields, excluding class id and
-  //    identity hash).
-  void generateInitializerList(Reference target) {
-    assert(target.isInitializerReference);
-    Constructor member = target.asConstructor;
+class TearOffCodeGenerator extends AstCodeGenerator {
+  final Member member;
 
-    setupInitializerListParametersAndContexts(target);
+  TearOffCodeGenerator(Translator translator, w.FunctionType functionType,
+      w.InstructionsBuilder b, this.member)
+      : super(translator, functionType, b, member);
 
-    Class cls = member.enclosingClass;
-    ClassInfo info = translator.classInfo[cls]!;
-
-    List<w.Local> initializedFields = _generateInitializers(member);
-    bool containsSuperInitializer = false;
-    bool containsRedirectingInitializer = false;
-
-    for (Initializer initializer in member.initializers) {
-      if (initializer is SuperInitializer) {
-        containsSuperInitializer = true;
-      } else if (initializer is RedirectingInitializer) {
-        containsRedirectingInitializer = true;
-      }
+  @override
+  void generateInternal() {
+    if (member is Constructor) {
+      // Closures are built when constructor functions are added to worklist.
+      closures = translator.constructorClosures[member.reference]!;
+    } else {
+      // Build closure information.
+      closures = Closures(translator, member);
     }
 
-    if (cls.superclass != null && !containsRedirectingInitializer) {
-      // checks if a SuperInitializer was dropped because the constructor body
-      // throws an error
-      if (!containsSuperInitializer) {
-        b.unreachable();
-        b.end();
-        return;
-      }
-
-      // checks if a FieldInitializer was dropped because the constructor body
-      // throws an error
-      for (Field field in info.cls!.fields) {
-        if (field.isInstanceMember && !fieldLocals.containsKey(field)) {
-          b.unreachable();
-          b.end();
-          return;
-        }
-      }
-    }
-
-    // push constructor arguments
-    List<w.Local> constructorArgs =
-        _getConstructorArgumentLocals(member.reference, true);
-
-    for (w.Local arg in constructorArgs) {
-      b.local_get(arg);
-    }
-
-    // push reference to context
-    Context? context = closures.contexts[member];
-    if (context != null) {
-      assert(!context.isEmpty);
-      b.local_get(context.currentLocal);
-    }
-
-    // push initialized fields
-    for (w.Local field in initializedFields) {
-      b.local_get(field);
-    }
-
-    b.end();
+    return generateTearOffGetter(member as Procedure);
   }
 
-  /// Generate type checker method for a setter.
-  ///
-  /// This function will be called by a setter forwarder in a dynamic set to
-  /// type check the setter argument before calling the actual setter.
-  void _generateFieldSetterTypeCheckerMethod() {
-    final receiverLocal = paramLocals[0];
-    final positionalArgLocal = paramLocals[1];
-
+  void generateTearOffGetter(Procedure procedure) {
     _initializeThis(member.reference);
+    DartType functionType = translator.getTearOffType(procedure);
+    ClosureImplementation closure = translator.getTearOffClosure(procedure);
+    w.StructType struct = closure.representation.closureStruct;
 
-    // Local for the argument.
-    final argLocal = addLocal(translator.topInfo.nullableType);
+    ClassInfo info = translator.closureInfo;
+    translator.functions.recordClassAllocation(info.classId);
 
-    final member_ = member;
-    DartType paramType;
-    if (member_ is Field) {
-      paramType = member_.type;
+    b.i32_const(info.classId);
+    b.i32_const(initialIdentityHash);
+    b.local_get(paramLocals[0]); // `this` as context
+    b.global_get(closure.vtable);
+    types.makeType(this, functionType);
+    b.struct_new(struct);
+    b.end();
+  }
+}
+
+class TypeCheckerCodeGenerator extends AstCodeGenerator {
+  final Member member;
+
+  TypeCheckerCodeGenerator(Translator translator, w.FunctionType functionType,
+      w.InstructionsBuilder b, this.member)
+      : super(translator, functionType, b, member);
+
+  @override
+  void generateInternal() {
+    if (member is Constructor) {
+      // Closures are built when constructor functions are added to worklist.
+      closures = translator.constructorClosures[member.reference]!;
     } else {
-      paramType = (member_ as Procedure).setterType;
+      // Build closure information.
+      closures = Closures(translator, member);
     }
 
-    if (!translator.options.omitImplicitTypeChecks) {
-      _generateArgumentTypeCheck(
-        member.name.text,
-        () => b.local_get(positionalArgLocal),
-        paramType,
-        argLocal,
-      );
-    }
-
-    ClassInfo info = translator.classInfo[member_.enclosingClass]!;
-    if (member_ is Field) {
-      int fieldIndex = translator.fieldIndex[member_]!;
-      b.local_get(receiverLocal);
-      translator.convertType(b, receiverLocal.type, info.nonNullableType);
-      b.local_get(argLocal);
-      translator.convertType(
-          b, argLocal.type, info.struct.fields[fieldIndex].type.unpacked);
-      b.struct_set(info.struct, fieldIndex);
+    if (member is Field ||
+        (member is Procedure && (member as Procedure).isSetter)) {
+      return _generateFieldSetterTypeCheckerMethod();
     } else {
-      final setterProcedure = member_ as Procedure;
-      final setterProcedureWasmType =
-          translator.signatureForDirectCall(setterProcedure.reference);
-      final setterWasmInputs = setterProcedureWasmType.inputs;
-      assert(setterWasmInputs.length == 2);
-      b.local_get(receiverLocal);
-      translator.convertType(b, receiverLocal.type, setterWasmInputs[0]);
-      b.local_get(argLocal);
-      translator.convertType(b, argLocal.type, setterWasmInputs[1]);
-      call(setterProcedure.reference);
+      return _generateProcedureTypeCheckerMethod();
     }
-
-    b.local_get(argLocal);
-    b.end(); // end function
   }
 
   /// Generate type checker method for a method.
@@ -3632,116 +3389,544 @@
     b.end();
   }
 
-  /// Generate code that checks type of an argument against an expected type
-  /// and throws a `TypeError` on failure.
+  /// Generate type checker method for a setter.
   ///
-  /// Does not expect any values on stack and does not leave any values on
-  /// stack.
-  ///
-  /// Locals [argLocal] are used to store values pushed by [pushArg]
-  /// and reuse the values.
-  ///
-  /// [argName] is used in the type error as the name of the argument that
-  /// doesn't match the expected type.
-  void _generateArgumentTypeCheck(
-    String argName,
-    void Function() pushArg,
-    DartType testedAgainstType,
-    w.Local argLocal,
-  ) {
-    if (translator.options.minify) {
-      // We don't need to include the name in the error message, so we can use
-      // the optimized `as` checks.
-      pushArg();
-      types.emitAsCheck(
-          this,
-          testedAgainstType,
-          translator.coreTypes.objectNullableRawType,
-          argLocal.type as w.RefType);
-      b.drop();
+  /// This function will be called by a setter forwarder in a dynamic set to
+  /// type check the setter argument before calling the actual setter.
+  void _generateFieldSetterTypeCheckerMethod() {
+    final receiverLocal = paramLocals[0];
+    final positionalArgLocal = paramLocals[1];
+
+    _initializeThis(member.reference);
+
+    // Local for the argument.
+    final argLocal = addLocal(translator.topInfo.nullableType);
+
+    final member_ = member;
+    DartType paramType;
+    if (member_ is Field) {
+      paramType = member_.type;
     } else {
-      pushArg();
-      b.local_tee(argLocal);
-      types.emitIsTest(
-          this, testedAgainstType, translator.coreTypes.objectNullableRawType);
-      b.i32_eqz();
-      b.if_();
-      b.local_get(argLocal);
-      types.makeType(this, testedAgainstType);
-      _emitString(argName);
-      call(translator.stackTraceCurrent.reference);
-      call(translator.throwArgumentTypeCheckError.reference);
-      b.unreachable();
-      b.end();
+      paramType = (member_ as Procedure).setterType;
     }
+
+    if (!translator.options.omitImplicitTypeChecks) {
+      _generateArgumentTypeCheck(
+        member.name.text,
+        () => b.local_get(positionalArgLocal),
+        paramType,
+        argLocal,
+      );
+    }
+
+    ClassInfo info = translator.classInfo[member_.enclosingClass]!;
+    if (member_ is Field) {
+      int fieldIndex = translator.fieldIndex[member_]!;
+      b.local_get(receiverLocal);
+      translator.convertType(b, receiverLocal.type, info.nonNullableType);
+      b.local_get(argLocal);
+      translator.convertType(
+          b, argLocal.type, info.struct.fields[fieldIndex].type.unpacked);
+      b.struct_set(info.struct, fieldIndex);
+    } else {
+      final setterProcedure = member_ as Procedure;
+      final setterProcedureWasmType =
+          translator.signatureForDirectCall(setterProcedure.reference);
+      final setterWasmInputs = setterProcedureWasmType.inputs;
+      assert(setterWasmInputs.length == 2);
+      b.local_get(receiverLocal);
+      translator.convertType(b, receiverLocal.type, setterWasmInputs[0]);
+      b.local_get(argLocal);
+      translator.convertType(b, argLocal.type, setterWasmInputs[1]);
+      call(setterProcedure.reference);
+    }
+
+    b.local_get(argLocal);
+    b.end(); // end function
+  }
+}
+
+class InitializerListCodeGenerator extends AstCodeGenerator {
+  final Constructor member;
+
+  InitializerListCodeGenerator(Translator translator,
+      w.FunctionType functionType, w.InstructionsBuilder b, this.member)
+      : super(translator, functionType, b, member);
+
+  @override
+  void generateInternal() {
+    // Closures are built when constructor functions are added to worklist.
+    closures = translator.constructorClosures[member.reference]!;
+
+    final source = member.enclosingComponent!.uriToSource[member.fileUri]!;
+    setSourceMapSourceAndFileOffset(source, member.fileOffset);
+
+    if (member.isExternal) {
+      emitUnimplementedExternalError(member);
+      b.end();
+      return;
+    }
+
+    generateInitializerList();
   }
 
-  void _generateTypeArgumentBoundCheck(
-    String argName,
-    w.Local typeLocal,
-    DartType bound,
-  ) {
-    b.local_get(typeLocal);
-    final boundLocal =
-        b.addLocal(translator.classInfo[translator.typeClass]!.nonNullableType);
-    types.makeType(this, bound);
-    b.local_tee(boundLocal);
-    call(translator.isTypeSubtype.reference);
+  // Generates a constructor's initializer list method, and returns:
+  // 1. Arguments and contexts returned from a super or redirecting initializer
+  //    method (in reverse order).
+  // 2. Arguments for this constructor (in reverse order).
+  // 3. A reference to the context for this constructor (or null if there is no
+  //    context).
+  // 4. Class fields (including superclass fields, excluding class id and
+  //    identity hash).
+  void generateInitializerList() {
+    _setupInitializerListParametersAndContexts();
 
-    b.i32_eqz();
-    b.if_();
-    // Type check failed
-    b.local_get(typeLocal);
-    b.local_get(boundLocal);
-    _emitString(argName);
-    call(translator.stackTraceCurrent.reference);
-    call(translator.throwTypeArgumentBoundCheckError.reference);
-    b.unreachable();
+    Class cls = member.enclosingClass;
+    ClassInfo info = translator.classInfo[cls]!;
+
+    List<w.Local> initializedFields = _generateInitializers(member);
+    bool containsSuperInitializer = false;
+    bool containsRedirectingInitializer = false;
+
+    for (Initializer initializer in member.initializers) {
+      if (initializer is SuperInitializer) {
+        containsSuperInitializer = true;
+      } else if (initializer is RedirectingInitializer) {
+        containsRedirectingInitializer = true;
+      }
+    }
+
+    if (cls.superclass != null && !containsRedirectingInitializer) {
+      // checks if a SuperInitializer was dropped because the constructor body
+      // throws an error
+      if (!containsSuperInitializer) {
+        b.unreachable();
+        b.end();
+        return;
+      }
+
+      // checks if a FieldInitializer was dropped because the constructor body
+      // throws an error
+      for (Field field in info.cls!.fields) {
+        if (field.isInstanceMember && !fieldLocals.containsKey(field)) {
+          b.unreachable();
+          b.end();
+          return;
+        }
+      }
+    }
+
+    // push constructor arguments
+    List<w.Local> constructorArgs =
+        _getConstructorArgumentLocals(member.reference, true);
+
+    for (w.Local arg in constructorArgs) {
+      b.local_get(arg);
+    }
+
+    // push reference to context
+    Context? context = closures.contexts[member];
+    if (context != null) {
+      assert(!context.isEmpty);
+      b.local_get(context.currentLocal);
+    }
+
+    // push initialized fields
+    for (w.Local field in initializedFields) {
+      b.local_get(field);
+    }
+
     b.end();
   }
 
-  void _emitString(String str) => wrap(StringLiteral(str),
-      translator.translateType(translator.coreTypes.stringNonNullableRawType));
-
-  @override
-  void visitPatternSwitchStatement(PatternSwitchStatement node) {
-    // This node is internal to the front end and removed by the constant
-    // evaluator.
-    throw UnsupportedError("CodeGenerator.visitPatternSwitchStatement");
+  void _setupInitializerListParametersAndContexts() {
+    setupParameters(member.initializerReference, isForwarder: true);
+    allocateContext(member);
+    captureParameters();
   }
 
-  @override
-  void visitPatternVariableDeclaration(PatternVariableDeclaration node) {
-    // This node is internal to the front end and removed by the constant
-    // evaluator.
-    throw UnsupportedError("CodeGenerator.visitPatternVariableDeclaration");
+  List<w.Local> _generateInitializers(Constructor member) {
+    Class cls = member.enclosingClass;
+    ClassInfo info = translator.classInfo[cls]!;
+    List<w.Local> superclassFields = [];
+
+    _setupDefaultFieldValues(info);
+
+    // Generate initializer list
+    for (Initializer initializer in member.initializers) {
+      visitInitializer(initializer);
+
+      if (initializer is SuperInitializer) {
+        // Save super classes' fields to locals
+        ClassInfo superInfo = info.superInfo!;
+
+        for (w.ValueType outputType
+            in superInfo.getClassFieldTypes().reversed) {
+          w.Local local = addLocal(outputType);
+          b.local_set(local);
+          superclassFields.add(local);
+        }
+      } else if (initializer is RedirectingInitializer) {
+        // Save redirected classes' fields to locals
+        List<w.Local> redirectedFields = [];
+
+        for (w.ValueType outputType in info.getClassFieldTypes().reversed) {
+          w.Local local = addLocal(outputType);
+          b.local_set(local);
+          redirectedFields.add(local);
+        }
+
+        return redirectedFields.reversed.toList();
+      }
+    }
+
+    List<w.Local> typeFields = [];
+
+    for (TypeParameter typeParam in cls.typeParameters) {
+      TypeParameter? match = info.typeParameterMatch[typeParam];
+
+      if (match == null) {
+        // Type is not contained in super class' fields
+        typeFields.add(typeLocals[typeParam]!);
+      }
+    }
+
+    List<w.Local> orderedFieldLocals = Map.fromEntries(
+            fieldLocals.entries.toList()
+              ..sort((x, y) => translator.fieldIndex[x.key]!
+                  .compareTo(translator.fieldIndex[y.key]!)))
+        .values
+        .toList();
+
+    return superclassFields.reversed.toList() + typeFields + orderedFieldLocals;
   }
+}
+
+class ConstructorAllocatorCodeGenerator extends AstCodeGenerator {
+  final Constructor member;
+
+  ConstructorAllocatorCodeGenerator(Translator translator,
+      w.FunctionType functionType, w.InstructionsBuilder b, this.member)
+      : super(translator, functionType, b, member);
 
   @override
-  void visitIfCaseStatement(IfCaseStatement node) {
-    // This node is internal to the front end and removed by the constant
-    // evaluator.
-    throw UnsupportedError("CodeGenerator.visitIfCaseStatement");
+  void generateInternal() {
+    // Closures are built when constructor functions are added to worklist.
+    closures = translator.constructorClosures[member.reference]!;
+
+    final source = member.enclosingComponent!.uriToSource[member.fileUri]!;
+    setSourceMapSourceAndFileOffset(source, member.fileOffset);
+
+    generateConstructorAllocator();
   }
 
-  void debugRuntimePrint(String s) {
-    final printFunction =
-        translator.functions.getFunction(translator.printToConsole.reference);
-    translator.constants.instantiateConstant(
-        b, StringConstant(s), printFunction.type.inputs[0]);
-    b.call(printFunction);
+  // Generates a function for allocating an object. This calls the separate
+  // initializer list and constructor body methods, and allocates a struct for
+  // the object.
+  void generateConstructorAllocator() {
+    setupParameters(member.reference, isForwarder: true);
+
+    w.FunctionType initializerMethodType =
+        translator.signatureForDirectCall(member.initializerReference);
+
+    List<w.Local> constructorArgs =
+        _getConstructorArgumentLocals(member.reference);
+
+    for (w.Local local in constructorArgs) {
+      b.local_get(local);
+    }
+
+    b.comment("Direct call of '$member Initializer'");
+    call(member.initializerReference);
+
+    ClassInfo info = translator.classInfo[member.enclosingClass]!;
+
+    // Add evaluated fields to locals
+    List<w.Local> orderedFieldLocals = [];
+
+    List<w.FieldType> fieldTypes = info.struct.fields
+        .sublist(FieldIndex.objectFieldBase)
+        .reversed
+        .toList();
+
+    for (w.FieldType field in fieldTypes) {
+      w.Local local = addLocal(field.type.unpacked);
+      orderedFieldLocals.add(local);
+      b.local_set(local);
+    }
+
+    Context? context = closures.contexts[member];
+    w.Local? contextLocal;
+
+    bool hasContext = context != null;
+
+    if (hasContext) {
+      assert(!context.isEmpty);
+      w.ValueType contextRef = w.RefType.struct(nullable: true);
+      contextLocal = addLocal(contextRef);
+      b.local_set(contextLocal);
+    }
+
+    List<w.ValueType> initializerOutputTypes = initializerMethodType.outputs;
+    int numConstructorBodyArgs = initializerOutputTypes.length -
+        fieldTypes.length -
+        (hasContext ? 1 : 0);
+
+    // Pop all arguments to constructor body
+    List<w.ValueType> constructorArgTypes =
+        initializerOutputTypes.sublist(0, numConstructorBodyArgs);
+
+    List<w.Local> constructorArguments = [];
+
+    for (w.ValueType argType in constructorArgTypes.reversed) {
+      w.Local local = addLocal(argType);
+      b.local_set(local);
+      constructorArguments.add(local);
+    }
+
+    // Set field values
+    b.i32_const(info.classId);
+    b.i32_const(initialIdentityHash);
+
+    for (w.Local local in orderedFieldLocals.reversed) {
+      b.local_get(local);
+    }
+
+    // create new struct with these fields and set to local
+    w.Local temp = addLocal(info.nonNullableType);
+    b.struct_new(info.struct);
+    b.local_tee(temp);
+
+    // Push context local if it is present
+    if (contextLocal != null) {
+      b.local_get(contextLocal);
+    }
+
+    // Push all constructor arguments
+    for (w.Local constructorArg in constructorArguments) {
+      b.local_get(constructorArg);
+    }
+
+    b.comment("Direct call of $member Constructor Body");
+    call(member.constructorBodyReference);
+
+    b.local_get(temp);
+    b.end();
   }
+}
+
+class ConstructorCodeGenerator extends AstCodeGenerator {
+  final Constructor member;
+
+  ConstructorCodeGenerator(Translator translator, w.FunctionType functionType,
+      w.InstructionsBuilder b, this.member)
+      : super(translator, functionType, b, member);
 
   @override
-  void visitAuxiliaryStatement(AuxiliaryStatement node) {
-    throw UnsupportedError(
-        "Unsupported auxiliary statement $node (${node.runtimeType}).");
+  void generateInternal() {
+    super.generate;
+    // Closures are built when constructor functions are added to worklist.
+    closures = translator.constructorClosures[member.reference]!;
+
+    final source = member.enclosingComponent!.uriToSource[member.fileUri]!;
+    setSourceMapSourceAndFileOffset(source, member.fileOffset);
+
+    generateConstructorBody();
   }
 
+  // Generates a function for a constructor's body, where the allocated struct
+  // object is passed to this function.
+  void generateConstructorBody() {
+    _setupConstructorBodyParametersAndContexts();
+
+    int getStartIndexForSuperOrRedirectedConstructorArguments() {
+      // Skips the receiver param and the current constructor's context
+      // (if it exists)
+      Context? context = closures.contexts[member];
+      bool hasContext = context != null;
+
+      if (hasContext) {
+        assert(!context.isEmpty);
+      }
+
+      int numSkippedParams = hasContext ? 2 : 1;
+
+      // Skips the current constructor's arguments
+      int numConstructorArgs =
+          _getConstructorArgumentLocals(member.constructorBodyReference).length;
+
+      return numSkippedParams + numConstructorArgs;
+    }
+
+    // Call super class' constructor body, or redirected constructor
+    for (Initializer initializer in member.initializers) {
+      if (initializer is SuperInitializer ||
+          initializer is RedirectingInitializer) {
+        Constructor target = initializer is SuperInitializer
+            ? initializer.target
+            : (initializer as RedirectingInitializer).target;
+
+        Supertype? supersupertype = target.enclosingClass.supertype;
+
+        if (supersupertype == null) {
+          break;
+        }
+
+        int startIndex =
+            getStartIndexForSuperOrRedirectedConstructorArguments();
+
+        List<w.Local> superOrRedirectedConstructorArgs =
+            paramLocals.sublist(startIndex);
+
+        w.Local object = thisLocal!;
+        b.local_get(object);
+
+        for (w.Local local in superOrRedirectedConstructorArgs) {
+          b.local_get(local);
+        }
+
+        call(target.constructorBodyReference);
+        break;
+      }
+    }
+
+    Statement? body = member.function.body;
+
+    if (body != null) {
+      visitStatement(body);
+    }
+
+    b.end();
+  }
+
+  void _setupConstructorBodyParametersAndContexts() {
+    ParameterInfo paramInfo =
+        translator.paramInfoForDirectCall(member.constructorBodyReference);
+
+    // For constructor body functions, the first parameter is always the
+    // receiver parameter, and the second parameter is a reference to the
+    // current context (if it exists).
+    Context? context = closures.contexts[member];
+    bool hasConstructorContext = context != null;
+
+    if (hasConstructorContext) {
+      assert(!context.isEmpty);
+      _initializeContextLocals(member, contextParamIndex: 1);
+    }
+
+    // Skips the receiver param (_initializeThis will return 1), and the
+    // context param if this exists.
+    int parameterOffset = _initializeThis(member.constructorBodyReference) +
+        (hasConstructorContext ? 1 : 0);
+    int implicitParams = parameterOffset + paramInfo.typeParamCount;
+
+    _setupLocalParameters(member, paramInfo, parameterOffset, implicitParams);
+    allocateContext(member.function);
+  }
+}
+
+class StaticFieldInitializerCodeGenerator extends AstCodeGenerator {
+  final Field field;
+
+  StaticFieldInitializerCodeGenerator(Translator translator,
+      w.FunctionType functionType, w.InstructionsBuilder b, this.field)
+      : super(translator, functionType, b, field);
+
   @override
-  void visitAuxiliaryInitializer(AuxiliaryInitializer node) {
-    throw UnsupportedError(
-        "Unsupported auxiliary initializer $node (${node.runtimeType}).");
+  void generateInternal() {
+    final source = field.enclosingComponent!.uriToSource[field.fileUri]!;
+    setSourceMapSourceAndFileOffset(source, field.fileOffset);
+
+    // Static field initializer function
+    closures = Closures(translator, field);
+    closures.findCaptures(field);
+    closures.collectContexts(field);
+    closures.buildContexts();
+
+    w.Global global = translator.globals.getGlobal(field);
+    w.Global? flag = translator.globals.getGlobalInitializedFlag(field);
+    wrap(field.initializer!, global.type.type);
+    b.global_set(global);
+    if (flag != null) {
+      b.i32_const(1);
+      b.global_set(flag);
+    }
+    b.global_get(global);
+    translator.convertType(b, global.type.type, outputs.single);
+    b.end();
+  }
+}
+
+class ImplicitFieldAccessorCodeGenerator extends AstCodeGenerator {
+  final Field field;
+  final bool isImplicitGetter;
+
+  ImplicitFieldAccessorCodeGenerator(
+    Translator translator,
+    w.FunctionType functionType,
+    w.InstructionsBuilder b,
+    this.field,
+    this.isImplicitGetter,
+  ) : super(translator, functionType, b, field);
+
+  @override
+  void generateInternal() {
+    closures = Closures(translator, field);
+
+    final source = field.enclosingComponent!.uriToSource[field.fileUri]!;
+    setSourceMapSourceAndFileOffset(source, field.fileOffset);
+
+    // Implicit getter or setter
+    w.StructType struct = translator.classInfo[field.enclosingClass!]!.struct;
+    int fieldIndex = translator.fieldIndex[field]!;
+    w.ValueType fieldType = struct.fields[fieldIndex].type.unpacked;
+
+    void getThis() {
+      w.Local thisLocal = paramLocals[0];
+      w.RefType structType = w.RefType.def(struct, nullable: false);
+      b.local_get(thisLocal);
+      translator.convertType(b, thisLocal.type, structType);
+    }
+
+    if (isImplicitGetter) {
+      // Implicit getter
+      getThis();
+      b.struct_get(struct, fieldIndex);
+      translator.convertType(b, fieldType, returnType);
+    } else {
+      // Implicit setter
+      w.Local valueLocal = paramLocals[1];
+      getThis();
+      b.local_get(valueLocal);
+      translator.convertType(b, valueLocal.type, fieldType);
+      b.struct_set(struct, fieldIndex);
+    }
+    b.end();
+  }
+}
+
+class SynchronousLambdaCodeGenerator extends AstCodeGenerator {
+  final Lambda lambda;
+  final Closures enclosingMemberClosures;
+
+  SynchronousLambdaCodeGenerator(Translator translator, Member enclosingMember,
+      this.lambda, this.enclosingMemberClosures)
+      : super(translator, lambda.function.type, lambda.function.body,
+            enclosingMember);
+
+  @override
+  void generateInternal() {
+    closures = enclosingMemberClosures;
+
+    setSourceMapSource(lambda.functionNodeSource);
+
+    assert(lambda.functionNode.asyncMarker != AsyncMarker.Async);
+
+    setupLambdaParametersAndContexts(lambda);
+
+    visitStatement(lambda.functionNode.body!);
+    _implicitReturn();
+    b.end();
   }
 }
 
@@ -3805,7 +3990,7 @@
   /// The `null: ...` case, if exists.
   late final SwitchCase? nullCase;
 
-  SwitchInfo(CodeGenerator codeGen, SwitchStatement node) {
+  SwitchInfo(AstCodeGenerator codeGen, SwitchStatement node) {
     final translator = codeGen.translator;
 
     final switchExprType = codeGen.dartTypeOf(node.expression);
diff --git a/pkg/dart2wasm/lib/intrinsics.dart b/pkg/dart2wasm/lib/intrinsics.dart
index df7261e..6d91ef7 100644
--- a/pkg/dart2wasm/lib/intrinsics.dart
+++ b/pkg/dart2wasm/lib/intrinsics.dart
@@ -12,14 +12,14 @@
 import 'translator.dart';
 import 'types.dart';
 
-typedef CodeGenCallback = void Function(CodeGenerator);
+typedef CodeGenCallback = void Function(AstCodeGenerator);
 
 /// Specialized code generation for external members.
 ///
 /// The code is generated either inlined at the call site, or as the body of
 /// the member in [generateMemberIntrinsic].
 class Intrinsifier {
-  final CodeGenerator codeGen;
+  final AstCodeGenerator codeGen;
 
   static const w.ValueType boolType = w.NumType.i32;
   static const w.ValueType intType = w.NumType.i64;
diff --git a/pkg/dart2wasm/lib/state_machine.dart b/pkg/dart2wasm/lib/state_machine.dart
index 84b43af..1a93fa8 100644
--- a/pkg/dart2wasm/lib/state_machine.dart
+++ b/pkg/dart2wasm/lib/state_machine.dart
@@ -563,40 +563,11 @@
   CatchVariables._(this.exception, this.stackTrace);
 }
 
-abstract class StateMachineEntryCodeGenerator extends CodeGenerator {
+abstract class StateMachineEntryAstCodeGenerator extends AstCodeGenerator {
   final w.FunctionBuilder function;
-
-  StateMachineEntryCodeGenerator(
-      Translator translator, this.function, Reference reference)
-      : super(translator, function.type, function.body, reference,
-            paramLocals: function.locals.toList());
-
-  @override
-  void generate() {
-    final source = member.enclosingComponent!.uriToSource[member.fileUri]!;
-    closures = Closures(translator, member);
-    setSourceMapSource(source);
-    setSourceMapFileOffset(member.fileOffset);
-    setupParametersAndContexts(member.reference);
-    _generateBody(member.function!, source);
-  }
-
-  @override
-  void generateLambda(Lambda lambda, Closures closures) {
-    final source = lambda.functionNodeSource;
-    this.closures = closures;
-    setSourceMapSource(source);
-    setSourceMapFileOffset(lambda.functionNode.fileOffset);
-    setupLambdaParametersAndContexts(lambda);
-    _generateBody(lambda.functionNode, source);
-  }
-
-  void _generateBody(FunctionNode functionNode, Source functionSource) {
-    Context? context = closures.contexts[functionNode];
-    if (context != null && context.isEmpty) context = context.parent;
-
-    generateOuter(functionNode, context, functionSource);
-  }
+  StateMachineEntryAstCodeGenerator(
+      Translator translator, Member enclosingMember, this.function)
+      : super(translator, function.type, function.body, enclosingMember);
 
   /// Generate the outer function.
   ///
@@ -610,12 +581,59 @@
       FunctionNode functionNode, Context? context, Source functionSource);
 }
 
+abstract class ProcedureStateMachineEntryCodeGenerator
+    extends StateMachineEntryAstCodeGenerator {
+  final Procedure member;
+
+  ProcedureStateMachineEntryCodeGenerator(
+      Translator translator, w.FunctionBuilder function, this.member)
+      : super(translator, member, function);
+
+  @override
+  void generateInternal() {
+    final source = member.enclosingComponent!.uriToSource[member.fileUri]!;
+    closures = Closures(translator, member);
+    setSourceMapSource(source);
+    setSourceMapFileOffset(member.fileOffset);
+    setupParametersAndContexts(member);
+
+    Context? context = closures.contexts[member.function];
+    if (context != null && context.isEmpty) context = context.parent;
+
+    generateOuter(member.function, context, source);
+  }
+}
+
+abstract class LambdaStateMachineEntryCodeGenerator
+    extends StateMachineEntryAstCodeGenerator {
+  final Lambda lambda;
+
+  LambdaStateMachineEntryCodeGenerator(Translator translator,
+      Member enclosingMember, this.lambda, Closures closures)
+      : super(translator, enclosingMember, lambda.function) {
+    this.closures = closures;
+  }
+
+  @override
+  void generateInternal() {
+    final source = lambda.functionNodeSource;
+    setSourceMapSource(source);
+    setSourceMapFileOffset(lambda.functionNode.fileOffset);
+    setupLambdaParametersAndContexts(lambda);
+
+    Context? context = closures.contexts[lambda.functionNode];
+    if (context != null && context.isEmpty) context = context.parent;
+
+    generateOuter(lambda.functionNode, context, source);
+  }
+}
+
 /// A [CodeGenerator] that compiles the function to a state machine based on
 /// the suspension points in the function (`await` expressions and `yield`
 /// statements).
 ///
 /// This is used to compile `async` and `sync*` functions.
-abstract class StateMachineCodeGenerator extends CodeGenerator {
+abstract class StateMachineCodeGenerator extends AstCodeGenerator {
   final w.FunctionBuilder function;
   final FunctionNode functionNode;
   final Source functionSource;
@@ -623,12 +641,11 @@
   StateMachineCodeGenerator(
       Translator translator,
       this.function,
-      Reference reference,
+      Member enclosingMember,
       this.functionNode,
       this.functionSource,
       Closures closures)
-      : super(translator, function.type, function.body, reference,
-            paramLocals: function.locals.toList()) {
+      : super(translator, function.type, function.body, enclosingMember) {
     this.closures = closures;
   }
 
@@ -667,7 +684,7 @@
   List<CatchVariables> catchVariableStack = [];
 
   @override
-  void generate() {
+  void generateInternal() {
     setSourceMapSource(functionSource);
     setSourceMapFileOffset(functionNode.fileOffset);
 
@@ -692,13 +709,6 @@
     generateInner(functionNode, context);
   }
 
-  @override
-  void generateLambda(Lambda lambda, Closures closures) {
-    // This is only invoked for the actual async/async*/sync* code generator and
-    // not for the (inner) state machine code generator.
-    throw UnsupportedError('This should not be reachable');
-  }
-
   /// Store the exception value emitted by [emitValue] in suspension state.
   /// [getSuspendStateCurrentException] should return the value even after
   /// suspending the function and continuing it later.
diff --git a/pkg/dart2wasm/lib/sync_star.dart b/pkg/dart2wasm/lib/sync_star.dart
index 9718a48..9a9457b 100644
--- a/pkg/dart2wasm/lib/sync_star.dart
+++ b/pkg/dart2wasm/lib/sync_star.dart
@@ -10,9 +10,7 @@
 import 'code_generator.dart';
 import 'state_machine.dart';
 
-class SyncStarCodeGenerator extends StateMachineEntryCodeGenerator {
-  SyncStarCodeGenerator(super.translator, super.function, super.reference);
-
+mixin SyncStarCodeGeneratorMixin on StateMachineEntryAstCodeGenerator {
   late final ClassInfo suspendStateInfo =
       translator.classInfo[translator.suspendStateClass]!;
 
@@ -42,9 +40,9 @@
     b.return_();
     b.end();
 
-    SyncStarStateMachineCodeGenerator(translator, resumeFun, reference,
+    SyncStarStateMachineCodeGenerator(translator, resumeFun, enclosingMember,
             functionNode, functionSource, closures)
-        .generate();
+        .generate(resumeFun.locals.toList(), null);
   }
 
   w.FunctionBuilder _defineInnerBodyFunction(FunctionNode functionNode) =>
@@ -61,6 +59,21 @@
           "${function.functionName} inner");
 }
 
+/// Generates code for sync* procedures.
+class SyncStarProcedureCodeGenerator
+    extends ProcedureStateMachineEntryCodeGenerator
+    with SyncStarCodeGeneratorMixin {
+  SyncStarProcedureCodeGenerator(
+      super.translator, super.function, super.enclosingMember);
+}
+
+/// Generates code for sync* closures.
+class SyncStarLambdaCodeGenerator extends LambdaStateMachineEntryCodeGenerator
+    with SyncStarCodeGeneratorMixin {
+  SyncStarLambdaCodeGenerator(
+      super.translator, super.enclosingMember, super.lambda, super.closures);
+}
+
 /// A specialized code generator for generating code for `sync*` functions.
 ///
 /// This will create an "outer" function which is a small function that just
@@ -80,7 +93,7 @@
   SyncStarStateMachineCodeGenerator(
       super.translator,
       super.function,
-      super.reference,
+      super.enclosingMember,
       super.functionNode,
       super.functionSource,
       super.closures);
diff --git a/pkg/dart2wasm/lib/translator.dart b/pkg/dart2wasm/lib/translator.dart
index ecb0bc9..b1d797c 100644
--- a/pkg/dart2wasm/lib/translator.dart
+++ b/pkg/dart2wasm/lib/translator.dart
@@ -369,9 +369,10 @@
         m.exports.export(canonicalName, function);
       }
 
-      final CodeGenerator codeGen = CodeGenerator.forFunction(
-          this, member.function, function as w.FunctionBuilder, reference);
-      codeGen.generate();
+      final functionBuilder = function as w.FunctionBuilder;
+      final codeGen = getMemberCodeGenerator(this, functionBuilder, reference);
+      codeGen.generate(functionBuilder.locals.toList(), null);
+      final memberClosures = codeGen.closures;
 
       if (options.printWasm) {
         print(function.type);
@@ -384,11 +385,10 @@
       // the lambda functions once, so we only generate lambdas when the
       // constructor initializer methods are generated.
       if (member is! Constructor || reference.isInitializerReference) {
-        for (Lambda lambda in codeGen.closures.lambdas.values) {
-          final lambdaFunction = lambda.function;
-          CodeGenerator.forFunction(
-                  this, lambda.functionNode, lambdaFunction, reference)
-              .generateLambda(lambda, codeGen.closures);
+        final enclosingMember = member;
+        for (Lambda lambda in memberClosures.lambdas.values) {
+          getLambdaCodeGenerator(this, lambda, enclosingMember, memberClosures)
+              .generate(lambda.function.locals.toList(), null);
           _printFunction(function, "$canonicalName (closure)");
         }
       }
@@ -1086,6 +1086,13 @@
         NodeCounter().countNodes(body) <= options.inliningLimit;
   }
 
+  CodeGenerator? getInliningCodeGenerator(
+      Reference target, w.FunctionType functionType, w.InstructionsBuilder b) {
+    if (!shouldInline(target)) return null;
+    return getInlinableMemberCodeGenerator(
+        this, AsyncMarker.Sync, functionType, b, target);
+  }
+
   T? getPragma<T>(Annotatable node, String name, [T? defaultValue]) {
     for (Expression annotation in node.annotations) {
       if (annotation is ConstantExpression) {
diff --git a/pkg/dart2wasm/lib/types.dart b/pkg/dart2wasm/lib/types.dart
index e70b8b2..4bdd5b4 100644
--- a/pkg/dart2wasm/lib/types.dart
+++ b/pkg/dart2wasm/lib/types.dart
@@ -166,7 +166,7 @@
 
   /// Allocates a `WasmArray<_Type>` from [types] and pushes it to the
   /// stack.
-  void _makeTypeArray(CodeGenerator codeGen, Iterable<DartType> types) {
+  void _makeTypeArray(AstCodeGenerator codeGen, Iterable<DartType> types) {
     if (types.every(isTypeConstant)) {
       translator.constants.instantiateConstant(codeGen.b,
           translator.constants.makeTypeArray(types), typeArrayExpectedType);
@@ -178,7 +178,7 @@
     }
   }
 
-  void _makeInterfaceType(CodeGenerator codeGen, InterfaceType type) {
+  void _makeInterfaceType(AstCodeGenerator codeGen, InterfaceType type) {
     final b = codeGen.b;
     ClassInfo typeInfo = translator.classInfo[type.classNode]!;
     b.i32_const(encodedNullability(type));
@@ -186,7 +186,7 @@
     _makeTypeArray(codeGen, type.typeArguments);
   }
 
-  void _makeRecordType(CodeGenerator codeGen, RecordType type) {
+  void _makeRecordType(AstCodeGenerator codeGen, RecordType type) {
     codeGen.b.i32_const(encodedNullability(type));
 
     final names = translator.constants.makeArrayOf(
@@ -236,14 +236,14 @@
     return FutureOrType(s, declaredNullability);
   }
 
-  void _makeFutureOrType(CodeGenerator codeGen, FutureOrType type) {
+  void _makeFutureOrType(AstCodeGenerator codeGen, FutureOrType type) {
     final b = codeGen.b;
     b.i32_const(encodedNullability(type));
     makeType(codeGen, type.typeArgument);
     codeGen.call(translator.createNormalizedFutureOrType.reference);
   }
 
-  void _makeFunctionType(CodeGenerator codeGen, FunctionType type) {
+  void _makeFunctionType(AstCodeGenerator codeGen, FunctionType type) {
     int typeParameterOffset = computeFunctionTypeParameterOffset(type);
     final b = codeGen.b;
     b.i32_const(encodedNullability(type));
@@ -298,7 +298,7 @@
   /// Makes a `_Type` object on the stack.
   /// TODO(joshualitt): Refactor this logic to remove the dependency on
   /// CodeGenerator.
-  w.ValueType makeType(CodeGenerator codeGen, DartType type) {
+  w.ValueType makeType(AstCodeGenerator codeGen, DartType type) {
     // Always ensure type is normalized before making a type.
     type = normalize(type);
     final b = codeGen.b;
@@ -377,8 +377,8 @@
   /// Emit code for testing a value against a Dart type. Expects the value on
   /// the stack as a (ref null #Top) and leaves the result on the stack as an
   /// i32.
-  void emitIsTest(
-      CodeGenerator codeGen, DartType testedAgainstType, DartType operandType,
+  void emitIsTest(AstCodeGenerator codeGen, DartType testedAgainstType,
+      DartType operandType,
       [Location? location]) {
     final b = codeGen.b;
     b.comment("type check against $testedAgainstType");
@@ -438,7 +438,7 @@
     }
   }
 
-  w.ValueType emitAsCheck(CodeGenerator codeGen, DartType testedAgainstType,
+  w.ValueType emitAsCheck(AstCodeGenerator codeGen, DartType testedAgainstType,
       DartType operandType, w.RefType boxedOperandType,
       [Location? location]) {
     final b = codeGen.b;
diff --git a/pkg/front_end/lib/src/base/local_scope.dart b/pkg/front_end/lib/src/base/local_scope.dart
index 18335b7..e9e15a5 100644
--- a/pkg/front_end/lib/src/base/local_scope.dart
+++ b/pkg/front_end/lib/src/base/local_scope.dart
@@ -94,6 +94,7 @@
   void _recordUse(String name, int charOffset) {}
 
   @override
+  // Coverage-ignore(suite): Not run.
   void forEachExtension(void Function(ExtensionBuilder) f) {
     _parent?.forEachExtension(f);
   }
@@ -160,6 +161,7 @@
   }
 
   @override
+  // Coverage-ignore(suite): Not run.
   Map<String, List<int>>? get usedNames => null;
 }
 
@@ -223,6 +225,7 @@
   ScopeKind get kind => _scope.kind;
 
   @override
+  // Coverage-ignore(suite): Not run.
   Iterable<Builder> get localVariables => const [];
 
   @override
@@ -231,6 +234,7 @@
   }
 
   @override
+  // Coverage-ignore(suite): Not run.
   Builder? lookupLocalVariable(String name) => null;
 
   @override
@@ -239,6 +243,7 @@
   }
 
   @override
+  // Coverage-ignore(suite): Not run.
   void forEachExtension(void Function(ExtensionBuilder) f) {
     _scope.forEachExtension(f);
   }
diff --git a/pkg/front_end/lib/src/base/name_space.dart b/pkg/front_end/lib/src/base/name_space.dart
index a8e2734..882bbff 100644
--- a/pkg/front_end/lib/src/base/name_space.dart
+++ b/pkg/front_end/lib/src/base/name_space.dart
@@ -373,8 +373,8 @@
       nameSpace._getables?.forEach(mergeMember);
     }
     if (nameSpace._setables != null) {
-      map = _setables ??= // Coverage-ignore(suite): Not run.
-          {};
+      // Coverage-ignore-block(suite): Not run.
+      map = _setables ??= {};
       nameSpace._setables?.forEach(mergeMember);
     }
     if (nameSpace._extensions != null) {
diff --git a/pkg/front_end/lib/src/base/scope.dart b/pkg/front_end/lib/src/base/scope.dart
index 1aabf30..fa77578 100644
--- a/pkg/front_end/lib/src/base/scope.dart
+++ b/pkg/front_end/lib/src/base/scope.dart
@@ -182,15 +182,8 @@
     required bool forStaticAccess}) {
   if (builder == null) return null;
   if (builder.next != null) {
-    return new AmbiguousBuilder(
-        name.isEmpty
-            ?
-            // Coverage-ignore(suite): Not run.
-            classNameOrDebugName
-            : name,
-        builder,
-        charOffset,
-        fileUri);
+    return new AmbiguousBuilder(name.isEmpty ? classNameOrDebugName : name,
+        builder, charOffset, fileUri);
   } else if (forStaticAccess && builder.isDeclarationInstanceMember) {
     return null;
   } else if (builder is MemberBuilder && builder.isConflictingSetter) {
@@ -308,6 +301,7 @@
   TypeParameterScope(this._parent, this._typeParameters);
 
   @override
+  // Coverage-ignore(suite): Not run.
   ScopeKind get kind => ScopeKind.typeParameters;
 
   @override
@@ -338,6 +332,7 @@
   }
 
   @override
+  // Coverage-ignore(suite): Not run.
   void forEachExtension(void Function(ExtensionBuilder) f) {
     _parent.forEachExtension(f);
   }
@@ -375,8 +370,12 @@
   @override
   Builder? lookupSetable(String name, int charOffset, Uri fileUri) {
     Builder? builder = normalizeLookup(
-        getable: _getables?[name],
-        setable: _setables?[name],
+        getable: _getables
+            // Coverage-ignore(suite): Not run.
+            ?[name],
+        setable: _setables
+            // Coverage-ignore(suite): Not run.
+            ?[name],
         name: name,
         charOffset: charOffset,
         fileUri: fileUri,
@@ -391,6 +390,7 @@
   }
 }
 
+// Coverage-ignore(suite): Not run.
 // TODO(johnniwinther): Use this instead of [SourceLibraryBuilderScope].
 class CompilationUnitScope extends BaseNameSpaceLookupScope {
   final CompilationUnit _compilationUnit;
@@ -447,6 +447,7 @@
     _local[name] = builder;
   }
 
+  // Coverage-ignore(suite): Not run.
   void addLocalMembers(Map<String, MemberBuilder> map) {
     _local.addAll(map);
   }
diff --git a/pkg/front_end/lib/src/dill/dill_class_builder.dart b/pkg/front_end/lib/src/dill/dill_class_builder.dart
index 133b6c2..bbf02a2 100644
--- a/pkg/front_end/lib/src/dill/dill_class_builder.dart
+++ b/pkg/front_end/lib/src/dill/dill_class_builder.dart
@@ -78,6 +78,7 @@
   }
 
   @override
+  // Coverage-ignore(suite): Not run.
   LookupScope get scope => _scope;
 
   @override
diff --git a/pkg/front_end/lib/src/dill/dill_extension_builder.dart b/pkg/front_end/lib/src/dill/dill_extension_builder.dart
index f4f0980..47577d9 100644
--- a/pkg/front_end/lib/src/dill/dill_extension_builder.dart
+++ b/pkg/front_end/lib/src/dill/dill_extension_builder.dart
@@ -89,6 +89,7 @@
   }
 
   @override
+  // Coverage-ignore(suite): Not run.
   LookupScope get scope => _scope;
 
   @override
diff --git a/pkg/front_end/lib/src/dill/dill_extension_type_declaration_builder.dart b/pkg/front_end/lib/src/dill/dill_extension_type_declaration_builder.dart
index 1399162..6fe9fea 100644
--- a/pkg/front_end/lib/src/dill/dill_extension_type_declaration_builder.dart
+++ b/pkg/front_end/lib/src/dill/dill_extension_type_declaration_builder.dart
@@ -149,6 +149,7 @@
   DillLibraryBuilder get libraryBuilder => parent as DillLibraryBuilder;
 
   @override
+  // Coverage-ignore(suite): Not run.
   LookupScope get scope => _scope;
 
   @override
diff --git a/pkg/front_end/lib/src/kernel/body_builder.dart b/pkg/front_end/lib/src/kernel/body_builder.dart
index 356a7a5..45fa913 100644
--- a/pkg/front_end/lib/src/kernel/body_builder.dart
+++ b/pkg/front_end/lib/src/kernel/body_builder.dart
@@ -1583,8 +1583,8 @@
       // Coverage-ignore-block(suite): Not run.
       temporaryParent = new ListLiteral(expressions);
     }
-    // Coverage-ignore(suite): Not run.
     performBacklogComputations();
+    // Coverage-ignore(suite): Not run.
     return temporaryParent != null ? temporaryParent.expressions : expressions;
   }
 
@@ -3424,6 +3424,7 @@
   }
 
   @override
+  // Coverage-ignore(suite): Not run.
   void handleLiteralIntWithSeparators(Token token) {
     debugEvent("LiteralIntWithSeparators");
 
@@ -4158,7 +4159,7 @@
       assert(conditionStatement is EmptyStatement);
     }
     if (entry is MapLiteralEntry) {
-      ForMapEntry result;
+      TreeNode result;
       if (variableOrExpression is PatternVariableDeclaration) {
         result = forest.createPatternForMapEntry(offsetForToken(forToken),
             patternVariableDeclaration: variableOrExpression,
@@ -4174,7 +4175,7 @@
       typeInferrer.assignedVariables.endNode(result);
       push(result);
     } else {
-      ForElement result;
+      TreeNode result;
       if (variableOrExpression is PatternVariableDeclaration) {
         result = forest.createPatternForElement(offsetForToken(forToken),
             patternVariableDeclaration: variableOrExpression,
@@ -4653,11 +4654,9 @@
       for (int i = 0; i < setOrMapEntries.length; ++i) {
         if (setOrMapEntries[i] is MapLiteralEntry) {
           mapEntries[i] = setOrMapEntries[i];
-        } else if (setOrMapEntries[i] is ControlFlowElement) {
+        } else {
           mapEntries[i] = convertToMapEntry(setOrMapEntries[i], this,
               typeInferrer.assignedVariables.reassignInfo);
-        } else {
-          mapEntries[i] = convertToErroneousMapEntry(setOrMapEntries[i], this);
         }
       }
       buildLiteralMap(typeArguments, constKeyword, leftBrace, mapEntries);
@@ -4753,6 +4752,7 @@
   }
 
   @override
+  // Coverage-ignore(suite): Not run.
   void handleLiteralDoubleWithSeparators(Token token) {
     debugEvent("LiteralDoubleWithSeparators");
 
@@ -10186,6 +10186,7 @@
   _BodyBuilderCloner(this.bodyBuilder);
 
   @override
+  // Coverage-ignore(suite): Not run.
   TreeNode visitStaticInvocation(StaticInvocation node) {
     if (node is FactoryConstructorInvocation) {
       FactoryConstructorInvocation result = new FactoryConstructorInvocation(
@@ -10193,16 +10194,13 @@
           isConst: node.isConst)
         ..hasBeenInferred = node.hasBeenInferred;
       return result;
-    }
-    // Coverage-ignore(suite): Not run.
-    else if (node is TypeAliasedFactoryInvocation) {
+    } else if (node is TypeAliasedFactoryInvocation) {
       TypeAliasedFactoryInvocation result = new TypeAliasedFactoryInvocation(
           node.typeAliasBuilder, node.target, clone(node.arguments),
           isConst: node.isConst)
         ..hasBeenInferred = node.hasBeenInferred;
       return result;
     }
-    // Coverage-ignore(suite): Not run.
     return super.visitStaticInvocation(node);
   }
 
@@ -10231,6 +10229,7 @@
   }
 }
 
+// Coverage-ignore(suite): Not run.
 /// Returns `true` if [node] is not part of its parent member.
 ///
 /// This computation is costly and should only be used in assertions to verify
@@ -10248,7 +10247,6 @@
   if (member == null) {
     return true;
   }
-  // Coverage-ignore-block(suite): Not run.
   _FindChildVisitor visitor = new _FindChildVisitor(node);
   member.accept(visitor);
   return !visitor.foundNode;
diff --git a/pkg/front_end/lib/src/kernel/body_builder_context.dart b/pkg/front_end/lib/src/kernel/body_builder_context.dart
index 93c362d..3c42c95 100644
--- a/pkg/front_end/lib/src/kernel/body_builder_context.dart
+++ b/pkg/front_end/lib/src/kernel/body_builder_context.dart
@@ -683,6 +683,7 @@
   }
 
   @override
+  // Coverage-ignore(suite): Not run.
   bool get hasFormalParameters => false;
 }
 
@@ -790,6 +791,7 @@
             inConstFields: inConstFields);
 
   @override
+  // Coverage-ignore(suite): Not run.
   bool get hasFormalParameters => true;
 }
 
@@ -879,6 +881,7 @@
   }
 
   @override
+  // Coverage-ignore(suite): Not run.
   bool get hasFormalParameters => true;
 }
 
@@ -907,6 +910,7 @@
   }
 
   @override
+  // Coverage-ignore(suite): Not run.
   bool get hasFormalParameters => true;
 }
 
@@ -938,6 +942,7 @@
   }
 
   @override
+  // Coverage-ignore(suite): Not run.
   bool get hasFormalParameters => true;
 }
 
@@ -967,6 +972,7 @@
   }
 
   @override
+  // Coverage-ignore(suite): Not run.
   bool get hasFormalParameters => true;
 }
 
diff --git a/pkg/front_end/lib/src/kernel/collections.dart b/pkg/front_end/lib/src/kernel/collections.dart
index 92e9cba..bcbf67c 100644
--- a/pkg/front_end/lib/src/kernel/collections.dart
+++ b/pkg/front_end/lib/src/kernel/collections.dart
@@ -133,30 +133,35 @@
   NullAwareElement(this.expression);
 
   @override
+  // Coverage-ignore(suite): Not run.
   MapLiteralEntry? toMapLiteralEntry(
       void Function(TreeNode from, TreeNode to) onConvertElement) {
     return unsupported("toMapLiteralEntry", fileOffset, getFileUri(this));
   }
 
   @override
+  // Coverage-ignore(suite): Not run.
   void toTextInternal(AstPrinter printer) {
     printer.write('?');
     printer.writeExpression(expression);
   }
 
   @override
+  // Coverage-ignore(suite): Not run.
   void transformChildren(Transformer v) {
     expression = v.transform(expression);
     expression.parent = this;
   }
 
   @override
+  // Coverage-ignore(suite): Not run.
   void transformOrRemoveChildren(RemovingTransformer v) {
     expression = v.transform(expression);
     expression.parent = this;
   }
 
   @override
+  // Coverage-ignore(suite): Not run.
   void visitChildren(Visitor v) {
     expression.accept(v);
   }
@@ -616,7 +621,10 @@
   }
 }
 
-mixin ControlFlowMapEntry implements MapLiteralEntry {
+/// Base class for all control-flow map entries.
+sealed class ControlFlowMapEntry implements MapLiteralEntry {}
+
+mixin ControlFlowMapEntryMixin implements MapLiteralEntry {
   @override
   Expression get key {
     throw new UnsupportedError('ControlFlowMapEntry.key getter');
@@ -652,7 +660,9 @@
 }
 
 /// A null-aware entry in a map literal.
-class NullAwareMapEntry extends TreeNode with ControlFlowMapEntry {
+class NullAwareMapEntry extends TreeNode
+    with ControlFlowMapEntryMixin
+    implements ControlFlowMapEntry {
   /// `true` if the key expression is null-aware, that is, marked with `?`.
   bool isKeyNullAware;
 
@@ -672,6 +682,7 @@
       required this.value});
 
   @override
+  // Coverage-ignore(suite): Not run.
   void toTextInternal(AstPrinter printer) {
     if (isKeyNullAware) {
       printer.write('?');
@@ -685,6 +696,7 @@
   }
 
   @override
+  // Coverage-ignore(suite): Not run.
   void transformChildren(Transformer v) {
     key = v.transform(key);
     key.parent = this;
@@ -693,6 +705,7 @@
   }
 
   @override
+  // Coverage-ignore(suite): Not run.
   void transformOrRemoveChildren(RemovingTransformer v) {
     key = v.transform(key);
     key.parent = this;
@@ -701,6 +714,7 @@
   }
 
   @override
+  // Coverage-ignore(suite): Not run.
   void visitChildren(Visitor v) {
     key.accept(v);
     value.accept(v);
@@ -708,7 +722,9 @@
 }
 
 /// A spread element in a map literal.
-class SpreadMapEntry extends TreeNode with ControlFlowMapEntry {
+class SpreadMapEntry extends TreeNode
+    with ControlFlowMapEntryMixin
+    implements ControlFlowMapEntry {
   Expression expression;
   bool isNullAware;
 
@@ -756,7 +772,9 @@
 }
 
 /// An 'if' element in a map literal.
-class IfMapEntry extends TreeNode with ControlFlowMapEntry {
+class IfMapEntry extends TreeNode
+    with ControlFlowMapEntryMixin
+    implements ControlFlowMapEntry {
   Expression condition;
   MapLiteralEntry then;
   MapLiteralEntry? otherwise;
@@ -820,11 +838,30 @@
   }
 }
 
+abstract interface class ForMapEntryBase implements TreeNode, MapLiteralEntry {
+  List<VariableDeclaration> get variables;
+
+  abstract Expression? condition;
+
+  List<Expression> get updates;
+
+  abstract MapLiteralEntry body;
+}
+
 /// A 'for' element in a map literal.
-class ForMapEntry extends TreeNode with ControlFlowMapEntry {
+class ForMapEntry extends TreeNode
+    with ControlFlowMapEntryMixin
+    implements ForMapEntryBase, ControlFlowMapEntry {
+  @override
   final List<VariableDeclaration> variables; // May be empty, but not null.
+
+  @override
   Expression? condition; // May be null.
+
+  @override
   final List<Expression> updates; // May be empty, but not null.
+
+  @override
   MapLiteralEntry body;
 
   ForMapEntry(this.variables, this.condition, this.updates, this.body) {
@@ -896,8 +933,64 @@
   }
 }
 
+class PatternForMapEntry extends TreeNode
+    with InternalTreeNode, ControlFlowMapEntryMixin
+    implements ForMapEntryBase, ControlFlowMapEntry {
+  PatternVariableDeclaration patternVariableDeclaration;
+  List<VariableDeclaration> intermediateVariables;
+
+  @override
+  final List<VariableDeclaration> variables;
+
+  @override
+  Expression? condition;
+
+  @override
+  final List<Expression> updates;
+
+  @override
+  MapLiteralEntry body;
+
+  PatternForMapEntry(
+      {required this.patternVariableDeclaration,
+      required this.intermediateVariables,
+      required this.variables,
+      required this.condition,
+      required this.updates,
+      required this.body});
+
+  @override
+  // Coverage-ignore(suite): Not run.
+  void toTextInternal(AstPrinter printer) {
+    patternVariableDeclaration.toTextInternal(printer);
+    printer.write('for (');
+    for (int index = 0; index < variables.length; index++) {
+      if (index > 0) {
+        printer.write(', ');
+      }
+      printer.writeVariableDeclaration(variables[index],
+          includeModifiersAndType: index == 0);
+    }
+    printer.write('; ');
+    if (condition != null) {
+      printer.writeExpression(condition!);
+    }
+    printer.write('; ');
+    printer.writeExpressions(updates);
+    printer.write(') ');
+    body.toTextInternal(printer);
+  }
+
+  @override
+  String toString() {
+    return "PatternForMapEntry(${toStringInternal()})";
+  }
+}
+
 /// A 'for-in' element in a map literal.
-class ForInMapEntry extends TreeNode with ControlFlowMapEntry {
+class ForInMapEntry extends TreeNode
+    with ControlFlowMapEntryMixin
+    implements ControlFlowMapEntry {
   VariableDeclaration variable; // Has no initializer.
   Expression iterable;
   Expression? syntheticAssignment; // May be null.
@@ -993,6 +1086,53 @@
   }
 }
 
+class IfCaseMapEntry extends TreeNode
+    with InternalTreeNode, ControlFlowMapEntryMixin
+    implements ControlFlowMapEntry {
+  Expression expression;
+  PatternGuard patternGuard;
+  MapLiteralEntry then;
+  MapLiteralEntry? otherwise;
+  List<Statement> prelude;
+
+  /// The type of the expression against which this pattern is matched.
+  ///
+  /// This is set during inference.
+  DartType? matchedValueType;
+
+  IfCaseMapEntry(
+      {required this.prelude,
+      required this.expression,
+      required this.patternGuard,
+      required this.then,
+      this.otherwise}) {
+    expression.parent = this;
+    patternGuard.parent = this;
+    then.parent = this;
+    otherwise?.parent = this;
+  }
+
+  @override
+  // Coverage-ignore(suite): Not run.
+  void toTextInternal(AstPrinter printer) {
+    printer.write('if (');
+    expression.toTextInternal(printer);
+    printer.write(' case ');
+    patternGuard.toTextInternal(printer);
+    printer.write(') ');
+    then.toTextInternal(printer);
+    if (otherwise != null) {
+      printer.write(' else ');
+      otherwise!.toTextInternal(printer);
+    }
+  }
+
+  @override
+  String toString() {
+    return "IfCaseMapEntry(${toStringInternal()})";
+  }
+}
+
 /// Convert [entry] to an [Expression], if possible. If [entry] cannot be
 /// converted an error reported through [helper] and an invalid expression is
 /// returned.
@@ -1006,76 +1146,83 @@
   void onConvertMapEntry(TreeNode from, TreeNode to), {
   DartType? actualType,
 }) {
-  if (entry is SpreadMapEntry) {
-    return new SpreadElement(entry.expression, isNullAware: entry.isNullAware)
-      ..elementType = actualType
-      ..fileOffset = entry.expression.fileOffset;
-  }
-  if (entry is IfMapEntry) {
-    IfElement result = new IfElement(
-        entry.condition,
-        convertToElement(entry.then, helper, onConvertMapEntry),
-        entry.otherwise == null
-            ? null
-            :
-            // Coverage-ignore(suite): Not run.
-            convertToElement(entry.otherwise!, helper, onConvertMapEntry))
-      ..fileOffset = entry.fileOffset;
-    onConvertMapEntry(entry, result);
-    return result;
-  }
-  if (entry is IfCaseMapEntry) {
-    IfCaseElement result = new IfCaseElement(
-        prelude: entry.prelude,
-        expression: entry.expression,
-        patternGuard: entry.patternGuard,
-        then: convertToElement(entry.then, helper, onConvertMapEntry),
-        otherwise: entry.otherwise == null
-            ? null
-            :
-            // Coverage-ignore(suite): Not run.
-            convertToElement(entry.otherwise!, helper, onConvertMapEntry))
-      ..matchedValueType = entry.matchedValueType
-      ..fileOffset = entry.fileOffset;
-    onConvertMapEntry(entry, result);
-    return result;
-  }
-  if (entry is ForMapEntry) {
-    if (entry is PatternForMapEntry) {
-      PatternForElement result = new PatternForElement(
-          patternVariableDeclaration: entry.patternVariableDeclaration,
-          intermediateVariables: entry.intermediateVariables,
-          variables: entry.variables,
-          condition: entry.condition,
-          updates: entry.updates,
-          body: convertToElement(entry.body, helper, onConvertMapEntry))
-        ..fileOffset = entry.fileOffset;
-      onConvertMapEntry(entry, result);
-      return result;
-    } else {
-      ForElement result = new ForElement(
-          entry.variables,
-          entry.condition,
-          entry.updates,
-          convertToElement(entry.body, helper, onConvertMapEntry))
-        ..fileOffset = entry.fileOffset;
-      onConvertMapEntry(entry, result);
-      return result;
+  if (entry is ControlFlowMapEntry) {
+    switch (entry) {
+      case SpreadMapEntry():
+        return new SpreadElement(entry.expression,
+            isNullAware: entry.isNullAware)
+          ..elementType = actualType
+          ..fileOffset = entry.expression.fileOffset;
+      case IfMapEntry():
+        IfElement result = new IfElement(
+            entry.condition,
+            convertToElement(entry.then, helper, onConvertMapEntry),
+            entry.otherwise == null
+                ? null
+                :
+                // Coverage-ignore(suite): Not run.
+                convertToElement(entry.otherwise!, helper, onConvertMapEntry))
+          ..fileOffset = entry.fileOffset;
+        onConvertMapEntry(entry, result);
+        return result;
+      case NullAwareMapEntry():
+        // Coverage-ignore(suite): Not run.
+        return _convertToErroneousElement(entry, helper);
+      case IfCaseMapEntry():
+        IfCaseElement result = new IfCaseElement(
+            prelude: entry.prelude,
+            expression: entry.expression,
+            patternGuard: entry.patternGuard,
+            then: convertToElement(entry.then, helper, onConvertMapEntry),
+            otherwise: entry.otherwise == null
+                ? null
+                :
+                // Coverage-ignore(suite): Not run.
+                convertToElement(entry.otherwise!, helper, onConvertMapEntry))
+          ..matchedValueType = entry.matchedValueType
+          ..fileOffset = entry.fileOffset;
+        onConvertMapEntry(entry, result);
+        return result;
+      case PatternForMapEntry():
+        PatternForElement result = new PatternForElement(
+            patternVariableDeclaration: entry.patternVariableDeclaration,
+            intermediateVariables: entry.intermediateVariables,
+            variables: entry.variables,
+            condition: entry.condition,
+            updates: entry.updates,
+            body: convertToElement(entry.body, helper, onConvertMapEntry))
+          ..fileOffset = entry.fileOffset;
+        onConvertMapEntry(entry, result);
+        return result;
+      case ForMapEntry():
+        ForElement result = new ForElement(
+            entry.variables,
+            entry.condition,
+            entry.updates,
+            convertToElement(entry.body, helper, onConvertMapEntry))
+          ..fileOffset = entry.fileOffset;
+        onConvertMapEntry(entry, result);
+        return result;
+      case ForInMapEntry():
+        ForInElement result = new ForInElement(
+            entry.variable,
+            entry.iterable,
+            entry.syntheticAssignment,
+            entry.expressionEffects,
+            convertToElement(entry.body, helper, onConvertMapEntry),
+            entry.problem,
+            isAsync: entry.isAsync)
+          ..fileOffset = entry.fileOffset;
+        onConvertMapEntry(entry, result);
+        return result;
     }
+  } else {
+    return _convertToErroneousElement(entry, helper);
   }
-  if (entry is ForInMapEntry) {
-    ForInElement result = new ForInElement(
-        entry.variable,
-        entry.iterable,
-        entry.syntheticAssignment,
-        entry.expressionEffects,
-        convertToElement(entry.body, helper, onConvertMapEntry),
-        entry.problem,
-        isAsync: entry.isAsync)
-      ..fileOffset = entry.fileOffset;
-    onConvertMapEntry(entry, result);
-    return result;
-  }
+}
+
+Expression _convertToErroneousElement(
+    MapLiteralEntry entry, InferenceHelper? helper) {
   Expression key = entry.key;
   if (key is InvalidExpression) {
     Expression value = entry.value;
@@ -1108,7 +1255,6 @@
       case IfCaseElement():
         return isConvertibleToMapEntry(element.then) &&
             (element.otherwise == null ||
-                // Coverage-ignore(suite): Not run.
                 isConvertibleToMapEntry(element.otherwise!));
       case ForElement():
         return isConvertibleToMapEntry(element.body);
@@ -1127,95 +1273,89 @@
 /// [onConvertElement] is called when a [ForElement], [ForInElement], or
 /// [IfElement] is converted to a [ForMapEntry], [ForInMapEntry], or
 /// [IfMapEntry], respectively.
-MapLiteralEntry convertToMapEntry(ControlFlowElement element,
-    InferenceHelper helper, void onConvertElement(TreeNode from, TreeNode to)) {
-  switch (element) {
-    case SpreadElement():
-      return new SpreadMapEntry(element.expression,
-          isNullAware: element.isNullAware)
-        ..fileOffset = element.expression.fileOffset;
-
-    case NullAwareElement():
-      return convertToErroneousMapEntry(element, helper);
-
-    case IfElement():
-      IfMapEntry result = new IfMapEntry(
-          element.condition,
-          _convertNestedToMapEntry(element.then, helper, onConvertElement),
-          element.otherwise == null
-              ? null
-              : _convertNestedToMapEntry(
-                  element.otherwise!, helper, onConvertElement))
-        ..fileOffset = element.fileOffset;
-      onConvertElement(element, result);
-      return result;
-
-    case IfCaseElement():
-      IfCaseMapEntry result = new IfCaseMapEntry(
-          prelude: [],
-          expression: element.expression,
-          patternGuard: element.patternGuard,
-          then:
-              _convertNestedToMapEntry(element.then, helper, onConvertElement),
-          otherwise: element.otherwise == null
-              ? null
-              :
-              // Coverage-ignore(suite): Not run.
-              _convertNestedToMapEntry(
-                  element.otherwise!, helper, onConvertElement))
-        ..matchedValueType = element.matchedValueType
-        ..fileOffset = element.fileOffset;
-      onConvertElement(element, result);
-      return result;
-
-    case PatternForElement():
-      PatternForMapEntry result = new PatternForMapEntry(
-          patternVariableDeclaration: element.patternVariableDeclaration,
-          intermediateVariables: element.intermediateVariables,
-          variables: element.variables,
-          condition: element.condition,
-          updates: element.updates,
-          body:
-              _convertNestedToMapEntry(element.body, helper, onConvertElement))
-        ..fileOffset = element.fileOffset;
-      onConvertElement(element, result);
-      return result;
-
-    case ForElement():
-      ForMapEntry result = new ForMapEntry(
-          element.variables,
-          element.condition,
-          element.updates,
-          _convertNestedToMapEntry(element.body, helper, onConvertElement))
-        ..fileOffset = element.fileOffset;
-      onConvertElement(element, result);
-      return result;
-
-    case ForInElement():
-      ForInMapEntry result = new ForInMapEntry(
-          element.variable,
-          element.iterable,
-          element.syntheticAssignment,
-          element.expressionEffects,
-          _convertNestedToMapEntry(element.body, helper, onConvertElement),
-          element.problem,
-          isAsync: element.isAsync)
-        ..fileOffset = element.fileOffset;
-      onConvertElement(element, result);
-      return result;
-  }
-}
-
-MapLiteralEntry _convertNestedToMapEntry(Expression element,
-    InferenceHelper helper, void onConvertElement(TreeNode from, TreeNode to)) {
+MapLiteralEntry convertToMapEntry(Expression element, InferenceHelper helper,
+    void onConvertElement(TreeNode from, TreeNode to)) {
   if (element is ControlFlowElement) {
-    return convertToMapEntry(element, helper, onConvertElement);
+    switch (element) {
+      case SpreadElement():
+        return new SpreadMapEntry(element.expression,
+            isNullAware: element.isNullAware)
+          ..fileOffset = element.expression.fileOffset;
+
+      case NullAwareElement():
+        // Coverage-ignore(suite): Not run.
+        return _convertToErroneousMapEntry(element, helper);
+
+      case IfElement():
+        IfMapEntry result = new IfMapEntry(
+            element.condition,
+            convertToMapEntry(element.then, helper, onConvertElement),
+            element.otherwise == null
+                ? null
+                : convertToMapEntry(
+                    element.otherwise!, helper, onConvertElement))
+          ..fileOffset = element.fileOffset;
+        onConvertElement(element, result);
+        return result;
+
+      case IfCaseElement():
+        IfCaseMapEntry result = new IfCaseMapEntry(
+            prelude: [],
+            expression: element.expression,
+            patternGuard: element.patternGuard,
+            then: convertToMapEntry(element.then, helper, onConvertElement),
+            otherwise: element.otherwise == null
+                ? null
+                : convertToMapEntry(
+                    element.otherwise!, helper, onConvertElement))
+          ..matchedValueType = element.matchedValueType
+          ..fileOffset = element.fileOffset;
+        onConvertElement(element, result);
+        return result;
+
+      case PatternForElement():
+        PatternForMapEntry result = new PatternForMapEntry(
+            patternVariableDeclaration: element.patternVariableDeclaration,
+            intermediateVariables: element.intermediateVariables,
+            variables: element.variables,
+            condition: element.condition,
+            updates: element.updates,
+            body: convertToMapEntry(element.body, helper, onConvertElement))
+          ..fileOffset = element.fileOffset;
+        onConvertElement(element, result);
+        return result;
+
+      case ForElement():
+        ForMapEntry result = new ForMapEntry(
+            element.variables,
+            element.condition,
+            element.updates,
+            convertToMapEntry(element.body, helper, onConvertElement))
+          ..fileOffset = element.fileOffset;
+        onConvertElement(element, result);
+        return result;
+
+      case ForInElement():
+        ForInMapEntry result = new ForInMapEntry(
+            element.variable,
+            element.iterable,
+            element.syntheticAssignment,
+            element.expressionEffects,
+            convertToMapEntry(element.body, helper, onConvertElement),
+            element.problem,
+            isAsync: element.isAsync)
+          ..fileOffset = element.fileOffset;
+        onConvertElement(element, result);
+        return result;
+    }
   } else {
-    return convertToErroneousMapEntry(element, helper);
+    // Coverage-ignore-block(suite): Not run.
+    return _convertToErroneousMapEntry(element, helper);
   }
 }
 
-MapLiteralEntry convertToErroneousMapEntry(
+// Coverage-ignore(suite): Not run.
+MapLiteralEntry _convertToErroneousMapEntry(
     Expression element, InferenceHelper helper) {
   return new MapLiteralEntry(
       helper.buildProblem(
diff --git a/pkg/front_end/lib/src/kernel/forest.dart b/pkg/front_end/lib/src/kernel/forest.dart
index ea1c766..f53374e 100644
--- a/pkg/front_end/lib/src/kernel/forest.dart
+++ b/pkg/front_end/lib/src/kernel/forest.dart
@@ -18,11 +18,13 @@
         ForInMapEntry,
         ForMapEntry,
         IfCaseElement,
+        IfCaseMapEntry,
         IfElement,
         IfMapEntry,
         NullAwareElement,
         NullAwareMapEntry,
         PatternForElement,
+        PatternForMapEntry,
         SpreadElement;
 
 import 'internal_ast.dart';
diff --git a/pkg/front_end/lib/src/kernel/internal_ast.dart b/pkg/front_end/lib/src/kernel/internal_ast.dart
index dec3e4f..f992118 100644
--- a/pkg/front_end/lib/src/kernel/internal_ast.dart
+++ b/pkg/front_end/lib/src/kernel/internal_ast.dart
@@ -32,7 +32,6 @@
 import '../type_inference/inference_results.dart';
 import '../type_inference/inference_visitor.dart';
 import '../type_inference/type_schema.dart' show UnknownType;
-import 'collections.dart';
 
 typedef SharedMatchContext = shared
     .MatchContext<TreeNode, Expression, Pattern, DartType, VariableDeclaration>;
@@ -3124,106 +3123,6 @@
   }
 }
 
-class IfCaseMapEntry extends TreeNode
-    with InternalTreeNode, ControlFlowMapEntry {
-  Expression expression;
-  PatternGuard patternGuard;
-  MapLiteralEntry then;
-  MapLiteralEntry? otherwise;
-  List<Statement> prelude;
-
-  /// The type of the expression against which this pattern is matched.
-  ///
-  /// This is set during inference.
-  DartType? matchedValueType;
-
-  IfCaseMapEntry(
-      {required this.prelude,
-      required this.expression,
-      required this.patternGuard,
-      required this.then,
-      this.otherwise}) {
-    expression.parent = this;
-    patternGuard.parent = this;
-    then.parent = this;
-    otherwise?.parent = this;
-  }
-
-  @override
-  // Coverage-ignore(suite): Not run.
-  void toTextInternal(AstPrinter printer) {
-    printer.write('if (');
-    expression.toTextInternal(printer);
-    printer.write(' case ');
-    patternGuard.toTextInternal(printer);
-    printer.write(') ');
-    then.toTextInternal(printer);
-    if (otherwise != null) {
-      printer.write(' else ');
-      otherwise!.toTextInternal(printer);
-    }
-  }
-
-  @override
-  String toString() {
-    return "IfCaseMapEntry(${toStringInternal()})";
-  }
-}
-
-class PatternForMapEntry extends TreeNode
-    with InternalTreeNode, ControlFlowMapEntry
-    implements ForMapEntry {
-  PatternVariableDeclaration patternVariableDeclaration;
-  List<VariableDeclaration> intermediateVariables;
-
-  @override
-  final List<VariableDeclaration> variables;
-
-  @override
-  Expression? condition;
-
-  @override
-  final List<Expression> updates;
-
-  @override
-  MapLiteralEntry body;
-
-  PatternForMapEntry(
-      {required this.patternVariableDeclaration,
-      required this.intermediateVariables,
-      required this.variables,
-      required this.condition,
-      required this.updates,
-      required this.body});
-
-  @override
-  // Coverage-ignore(suite): Not run.
-  void toTextInternal(AstPrinter printer) {
-    patternVariableDeclaration.toTextInternal(printer);
-    printer.write('for (');
-    for (int index = 0; index < variables.length; index++) {
-      if (index > 0) {
-        printer.write(', ');
-      }
-      printer.writeVariableDeclaration(variables[index],
-          includeModifiersAndType: index == 0);
-    }
-    printer.write('; ');
-    if (condition != null) {
-      printer.writeExpression(condition!);
-    }
-    printer.write('; ');
-    printer.writeExpressions(updates);
-    printer.write(') ');
-    body.toTextInternal(printer);
-  }
-
-  @override
-  String toString() {
-    return "PatternForMapEntry(${toStringInternal()})";
-  }
-}
-
 /// Data structure used by the body builder in place of [ObjectPattern], to
 /// allow additional information to be captured that is needed during type
 /// inference.
@@ -3297,6 +3196,7 @@
   }
 
   @override
+  // Coverage-ignore(suite): Not run.
   void transformChildren(Transformer v) {
     value = v.transform(value)..parent = this;
   }
diff --git a/pkg/front_end/lib/src/kernel/kernel_constants.dart b/pkg/front_end/lib/src/kernel/kernel_constants.dart
index 4acd403..d23ff515 100644
--- a/pkg/front_end/lib/src/kernel/kernel_constants.dart
+++ b/pkg/front_end/lib/src/kernel/kernel_constants.dart
@@ -15,6 +15,7 @@
   KernelConstantErrorReporter(this.loader);
 
   @override
+  // Coverage-ignore(suite): Not run.
   bool get supportsTrackingReportedErrors => true;
 
   @override
diff --git a/pkg/front_end/lib/src/kernel/kernel_target.dart b/pkg/front_end/lib/src/kernel/kernel_target.dart
index 526a03b..3572a2c 100644
--- a/pkg/front_end/lib/src/kernel/kernel_target.dart
+++ b/pkg/front_end/lib/src/kernel/kernel_target.dart
@@ -1812,7 +1812,9 @@
 
   void readPatchFiles(SourceLibraryBuilder libraryBuilder,
       CompilationUnit compilationUnit, Uri originImportUri) {
-    assert(originImportUri.isScheme("dart"),
+    assert(
+        originImportUri.isScheme("dart"),
+        // Coverage-ignore(suite): Not run.
         "Unexpected origin import uri: $originImportUri");
     List<Uri>? patches = uriTranslator.getDartPatches(originImportUri.path);
     if (patches != null) {
diff --git a/pkg/front_end/lib/src/kernel/try_constant_evaluator.dart b/pkg/front_end/lib/src/kernel/try_constant_evaluator.dart
index 4cabaea..668e55b 100644
--- a/pkg/front_end/lib/src/kernel/try_constant_evaluator.dart
+++ b/pkg/front_end/lib/src/kernel/try_constant_evaluator.dart
@@ -104,9 +104,11 @@
   _ErrorReporter(this._reportError);
 
   @override
+  // Coverage-ignore(suite): Not run.
   bool get supportsTrackingReportedErrors => false;
 
   @override
+  // Coverage-ignore(suite): Not run.
   bool get hasSeenError {
     return unsupported("_ErrorReporter.hasSeenError", -1, null);
   }
diff --git a/pkg/front_end/lib/src/kernel/verifier.dart b/pkg/front_end/lib/src/kernel/verifier.dart
index edc88d7..f7ff87c 100644
--- a/pkg/front_end/lib/src/kernel/verifier.dart
+++ b/pkg/front_end/lib/src/kernel/verifier.dart
@@ -33,7 +33,6 @@
   return listener.errors;
 }
 
-// Coverage-ignore(suite): Not run.
 class FastaVerificationErrorListener implements VerificationErrorListener {
   final CompilerContext compilerContext;
   List<LocatedMessage> errors = [];
@@ -41,6 +40,7 @@
   FastaVerificationErrorListener(this.compilerContext);
 
   @override
+  // Coverage-ignore(suite): Not run.
   void reportError(String details,
       {required TreeNode? node,
       required Uri? problemUri,
diff --git a/pkg/front_end/lib/src/kernel/wildcard_lowering.dart b/pkg/front_end/lib/src/kernel/wildcard_lowering.dart
index adf1d08..c37fdf4 100644
--- a/pkg/front_end/lib/src/kernel/wildcard_lowering.dart
+++ b/pkg/front_end/lib/src/kernel/wildcard_lowering.dart
@@ -27,12 +27,14 @@
   return name.startsWith(wildcardPrefix) && name.endsWith(wildcardFormalSuffix);
 }
 
+// Coverage-ignore(suite): Not run.
 /// Whether the given [name] is a wildcard type variable.
 bool isWildcardLoweredTypeVariable(String name) {
   return name.startsWith(wildcardPrefix) &&
       name.endsWith(wildcardTypeVariableSuffix);
 }
 
+// Coverage-ignore(suite): Not run.
 /// Whether the given [name] is a wildcard variable.
 bool isWildcardLoweredVariable(String name) {
   return name.startsWith(wildcardPrefix) &&
diff --git a/pkg/front_end/lib/src/macros/macro_injected_impl.dart b/pkg/front_end/lib/src/macros/macro_injected_impl.dart
index 7a34233..ed94989 100644
--- a/pkg/front_end/lib/src/macros/macro_injected_impl.dart
+++ b/pkg/front_end/lib/src/macros/macro_injected_impl.dart
@@ -13,6 +13,7 @@
 /// If set, overrides the CFE's macro implementation.
 MacroImplementation? macroImplementation;
 
+// Coverage-ignore(suite): Not run.
 /// An injected macro implementation.
 class MacroImplementation {
   final MacroPackageConfigs packageConfigs;
diff --git a/pkg/front_end/lib/src/source/source_builder_mixins.dart b/pkg/front_end/lib/src/source/source_builder_mixins.dart
index 8201a79..3ed7c07 100644
--- a/pkg/front_end/lib/src/source/source_builder_mixins.dart
+++ b/pkg/front_end/lib/src/source/source_builder_mixins.dart
@@ -265,6 +265,7 @@
     }
 
     if (arguments != null && arguments.length != typeVariablesCount) {
+      // Coverage-ignore-block(suite): Not run.
       assert(libraryBuilder.loader.assertProblemReportedElsewhere(
           "SourceDeclarationBuilderMixin.buildAliasedTypeArguments: "
           "the numbers of type parameters and type arguments don't match.",
diff --git a/pkg/front_end/lib/src/source/source_class_builder.dart b/pkg/front_end/lib/src/source/source_class_builder.dart
index d2b4cf6..c073e9d 100644
--- a/pkg/front_end/lib/src/source/source_class_builder.dart
+++ b/pkg/front_end/lib/src/source/source_class_builder.dart
@@ -519,6 +519,7 @@
     }
 
     if (arguments != null && arguments.length != typeVariablesCount) {
+      // Coverage-ignore-block(suite): Not run.
       assert(libraryBuilder.loader.assertProblemReportedElsewhere(
           "SourceClassBuilder.buildAliasedTypeArguments: "
           "the numbers of type parameters and type arguments don't match.",
diff --git a/pkg/front_end/lib/src/source/source_compilation_unit.dart b/pkg/front_end/lib/src/source/source_compilation_unit.dart
index 55bcc86..b3af465 100644
--- a/pkg/front_end/lib/src/source/source_compilation_unit.dart
+++ b/pkg/front_end/lib/src/source/source_compilation_unit.dart
@@ -335,6 +335,7 @@
         "Source library builder as already been created for $this.");
     _libraryBuilder = _sourceLibraryBuilder;
     if (isPart) {
+      // Coverage-ignore-block(suite): Not run.
       // This is a part with no enclosing library.
       addProblem(messagePartOrphan, 0, 1, fileUri);
       _clearPartsAndReportExporters();
@@ -727,7 +728,9 @@
 
   @override
   void addImportsToScope() {
-    bool hasCoreImport = originImportUri == dartCore && !forPatchLibrary;
+    bool hasCoreImport = originImportUri == dartCore &&
+        // Coverage-ignore(suite): Not run.
+        !forPatchLibrary;
     for (Import import in _builderFactoryResult.imports) {
       if (import.importedCompilationUnit?.isPart ?? false) {
         // Coverage-ignore-block(suite): Not run.
diff --git a/pkg/front_end/lib/src/source/source_constructor_builder.dart b/pkg/front_end/lib/src/source/source_constructor_builder.dart
index 2572203..6e14f66 100644
--- a/pkg/front_end/lib/src/source/source_constructor_builder.dart
+++ b/pkg/front_end/lib/src/source/source_constructor_builder.dart
@@ -596,6 +596,7 @@
         if (declaration is ClassBuilder) {
           superclassBuilder = declaration;
         } else {
+          // Coverage-ignore-block(suite): Not run.
           assert(libraryBuilder.loader.assertProblemReportedElsewhere(
               "DeclaredSourceConstructorBuilder._computeSuperTargetBuilder: "
               "Unaliased 'declaration' isn't a ClassBuilder.",
@@ -610,6 +611,7 @@
         return null;
       }
     } else {
+      // Coverage-ignore-block(suite): Not run.
       assert(libraryBuilder.loader.assertProblemReportedElsewhere(
           "DeclaredSourceConstructorBuilder._computeSuperTargetBuilder: "
           "'supertype' isn't a NamedTypeBuilder.",
@@ -641,6 +643,7 @@
     if (constructorBuilder is ConstructorBuilder) {
       return constructorBuilder;
     } else {
+      // Coverage-ignore-block(suite): Not run.
       assert(libraryBuilder.loader.assertProblemReportedElsewhere(
           "DeclaredSourceConstructorBuilder._computeSuperTargetBuilder: "
           "Can't find a constructor with name '${superTarget.name.text}' in "
@@ -775,6 +778,7 @@
               (positionalSuperParameters ??= <int?>[]).add(null);
             }
           } else {
+            // Coverage-ignore-block(suite): Not run.
             assert(libraryBuilder.loader.assertProblemReportedElsewhere(
                 "DeclaredSourceConstructorBuilder"
                 ".finalizeSuperInitializingFormals: "
diff --git a/pkg/front_end/lib/src/source/source_enum_builder.dart b/pkg/front_end/lib/src/source/source_enum_builder.dart
index 2abf5d9..c2cf43a 100644
--- a/pkg/front_end/lib/src/source/source_enum_builder.dart
+++ b/pkg/front_end/lib/src/source/source_enum_builder.dart
@@ -134,6 +134,7 @@
       ConstructorScope constructorScope) {
     final int startCharOffsetComputed =
         metadata == null ? startCharOffset : metadata.first.charOffset;
+    // Coverage-ignore(suite): Not run.
     supertypeBuilder ??= new NamedTypeBuilderImpl(
         const PredefinedTypeName("_Enum"), const NullabilityBuilder.omitted(),
         instanceTypeVariableAccess: InstanceTypeVariableAccessState.Unexpected);
@@ -186,7 +187,6 @@
     objectType = new NamedTypeBuilderImpl(
         const PredefinedTypeName("Object"), const NullabilityBuilder.omitted(),
         instanceTypeVariableAccess: InstanceTypeVariableAccessState.Unexpected);
-    // Coverage-ignore(suite): Not run.
     selfType = new NamedTypeBuilderImpl(new SyntheticTypeName(name, charOffset),
         const NullabilityBuilder.omitted(),
         instanceTypeVariableAccess: InstanceTypeVariableAccessState.Unexpected,
diff --git a/pkg/front_end/lib/src/source/source_library_builder.dart b/pkg/front_end/lib/src/source/source_library_builder.dart
index 068f780..e87cf1f 100644
--- a/pkg/front_end/lib/src/source/source_library_builder.dart
+++ b/pkg/front_end/lib/src/source/source_library_builder.dart
@@ -2172,6 +2172,7 @@
     if (typeArguments.isEmpty) return;
 
     if (functionType.typeParameters.length != typeArguments.length) {
+      // Coverage-ignore-block(suite): Not run.
       assert(loader.assertProblemReportedElsewhere(
           "SourceLibraryBuilder.checkBoundsInInstantiation: "
           "the numbers of type parameters and type arguments don't match.",
diff --git a/pkg/front_end/lib/src/source/source_loader.dart b/pkg/front_end/lib/src/source/source_loader.dart
index 48d8dd8..cdaee95 100644
--- a/pkg/front_end/lib/src/source/source_loader.dart
+++ b/pkg/front_end/lib/src/source/source_loader.dart
@@ -254,6 +254,7 @@
     };
     assert(
         expectedFutureProblemsForCurrentPhase.isEmpty || hasSeenError,
+        // Coverage-ignore(suite): Not run.
         "Expected problems to be reported, but there were none.\n"
         "Current compilation phase: ${currentPhase}\n"
         "Expected at these locations:\n"
@@ -1477,9 +1478,8 @@
     }
     _sourceLibraryBuilders = sourceLibraries;
     assert(
-        _compilationUnits.values.every(
-            (compilationUnit) => !(compilationUnit is SourceCompilationUnit &&
-                // Coverage-ignore(suite): Not run.
+        _compilationUnits.values.every((compilationUnit) =>
+            !(compilationUnit is SourceCompilationUnit &&
                 compilationUnit.isAugmenting)),
         // Coverage-ignore(suite): Not run.
         "Augmentation library found in libraryBuilders: " +
diff --git a/pkg/front_end/lib/src/source/source_type_alias_builder.dart b/pkg/front_end/lib/src/source/source_type_alias_builder.dart
index 4325d2c..e25255f 100644
--- a/pkg/front_end/lib/src/source/source_type_alias_builder.dart
+++ b/pkg/front_end/lib/src/source/source_type_alias_builder.dart
@@ -303,6 +303,7 @@
     }
 
     if (arguments != null && arguments.length != typeVariablesCount) {
+      // Coverage-ignore-block(suite): Not run.
       assert(libraryBuilder.loader.assertProblemReportedElsewhere(
           "SourceTypeAliasBuilder.buildAliasedTypeArguments: "
           "the numbers of type parameters and type arguments don't match.",
diff --git a/pkg/front_end/lib/src/source/type_parameter_scope_builder.dart b/pkg/front_end/lib/src/source/type_parameter_scope_builder.dart
index bb0c6e4..59fe74d 100644
--- a/pkg/front_end/lib/src/source/type_parameter_scope_builder.dart
+++ b/pkg/front_end/lib/src/source/type_parameter_scope_builder.dart
@@ -429,11 +429,19 @@
 
   void addLocalMember(String name, MemberBuilder builder,
       {required bool setter}) {
-    (setter ? _setables : _getables)![name] = builder;
+    (setter
+        ?
+        // Coverage-ignore(suite): Not run.
+        _setables
+        : _getables)![name] = builder;
   }
 
   MemberBuilder? lookupLocalMember(String name, {required bool setter}) {
-    return (setter ? _setables : _getables)![name] as MemberBuilder?;
+    return (setter
+        ?
+        // Coverage-ignore(suite): Not run.
+        _setables
+        : _getables)![name] as MemberBuilder?;
   }
 
   NameSpace buildNameSpace(IDeclarationBuilder parent) {
@@ -448,7 +456,6 @@
       if (_typeVariables != null) {
         NominalVariableBuilder? tv = _typeVariables![name];
         if (tv != null) {
-          // Coverage-ignore-block(suite): Not run.
           parent.addProblem(
               templateConflictsWithTypeVariable.withArguments(name),
               member.charOffset,
diff --git a/pkg/front_end/lib/src/type_inference/inference_visitor.dart b/pkg/front_end/lib/src/type_inference/inference_visitor.dart
index 1dab4ce..ada0c75 100644
--- a/pkg/front_end/lib/src/type_inference/inference_visitor.dart
+++ b/pkg/front_end/lib/src/type_inference/inference_visitor.dart
@@ -41,12 +41,15 @@
         ForInElement,
         ForInMapEntry,
         ForMapEntry,
+        ForMapEntryBase,
         IfCaseElement,
+        IfCaseMapEntry,
         IfElement,
         IfMapEntry,
         NullAwareElement,
         NullAwareMapEntry,
         PatternForElement,
+        PatternForMapEntry,
         SpreadElement,
         SpreadMapEntry,
         convertToElement;
@@ -2772,6 +2775,7 @@
         }
       case NullAwareElement(:Expression expression):
         if (expression is ControlFlowElement) {
+          // Coverage-ignore-block(suite): Not run.
           checkElement(expression, item, typeArgument, inferredSpreadTypes,
               inferredConditionTypes);
         }
@@ -2790,6 +2794,7 @@
               inferredConditionTypes);
         }
         if (otherwise is ControlFlowElement) {
+          // Coverage-ignore-block(suite): Not run.
           checkElement(otherwise, item, typeArgument, inferredSpreadTypes,
               inferredConditionTypes);
         }
@@ -3456,28 +3461,30 @@
       DartType valueType,
       VariableDeclaration result,
       List<Statement> body) {
-    if (entry is SpreadMapEntry) {
-      _translateSpreadEntry(
-          entry, receiverType, keyType, valueType, result, body);
-    } else if (entry is NullAwareMapEntry) {
-      _translateNullAwareMapEntry(
-          entry, receiverType, keyType, valueType, result, body);
-    } else if (entry is IfMapEntry) {
-      _translateIfEntry(entry, receiverType, keyType, valueType, result, body);
-    } else if (entry is IfCaseMapEntry) {
-      _translateIfCaseEntry(
-          entry, receiverType, keyType, valueType, result, body);
-    } else if (entry is ForMapEntry) {
-      if (entry is PatternForMapEntry) {
-        _translatePatternForEntry(
-            entry, receiverType, keyType, valueType, result, body);
-      } else {
-        _translateForEntry(
-            entry, receiverType, keyType, valueType, result, body);
+    if (entry is ControlFlowMapEntry) {
+      switch (entry) {
+        case SpreadMapEntry():
+          _translateSpreadEntry(
+              entry, receiverType, keyType, valueType, result, body);
+        case NullAwareMapEntry():
+          _translateNullAwareMapEntry(
+              entry, receiverType, keyType, valueType, result, body);
+        case IfMapEntry():
+          _translateIfEntry(
+              entry, receiverType, keyType, valueType, result, body);
+        case IfCaseMapEntry():
+          _translateIfCaseEntry(
+              entry, receiverType, keyType, valueType, result, body);
+        case PatternForMapEntry():
+          _translatePatternForEntry(
+              entry, receiverType, keyType, valueType, result, body);
+        case ForMapEntry():
+          _translateForEntry(
+              entry, receiverType, keyType, valueType, result, body);
+        case ForInMapEntry():
+          _translateForInEntry(
+              entry, receiverType, keyType, valueType, result, body);
       }
-    } else if (entry is ForInMapEntry) {
-      _translateForInEntry(
-          entry, receiverType, keyType, valueType, result, body);
     } else {
       _addNormalEntry(entry, receiverType, result, body);
     }
@@ -3899,6 +3906,7 @@
             }
           case NullAwareElement():
             if (currentPart != null) {
+              // Coverage-ignore-block(suite): Not run.
               parts.add(makeLiteral(node.fileOffset, currentPart));
               currentPart = null;
             }
@@ -3908,8 +3916,8 @@
                 makeLiteral(element.fileOffset, []), iterableType,
                 nullCheckedValue: makeLiteral(element.fileOffset,
                     [_createNullCheckedVariableGet(temp)])));
+          // Coverage-ignore(suite): Not run.
           case IfElement():
-            // Coverage-ignore-block(suite): Not run.
             if (currentPart != null) {
               parts.add(makeLiteral(node.fileOffset, currentPart));
               currentPart = null;
@@ -3923,10 +3931,10 @@
                 : makeLiteral(element.fileOffset, []);
             parts.add(_createConditionalExpression(
                 element.fileOffset, condition, then, otherwise, iterableType));
+          // Coverage-ignore(suite): Not run.
           case IfCaseElement():
           case ForElement():
           case ForInElement():
-            // Coverage-ignore-block(suite): Not run.
             // Rejected earlier.
             problems.unhandled("${element.runtimeType}",
                 "_translateConstListOrSet", element.fileOffset, helper.uri);
@@ -3975,100 +3983,108 @@
 
     for (; i < node.entries.length; ++i) {
       MapLiteralEntry entry = node.entries[i];
-      if (entry is SpreadMapEntry) {
-        if (currentPart != null) {
-          parts.add(makeLiteral(node.fileOffset, currentPart));
-          currentPart = null;
+      if (entry is ControlFlowMapEntry) {
+        switch (entry) {
+          case SpreadMapEntry():
+            if (currentPart != null) {
+              parts.add(makeLiteral(node.fileOffset, currentPart));
+              currentPart = null;
+            }
+            Expression spreadExpression = entry.expression;
+            if (entry.isNullAware) {
+              VariableDeclaration temp = _createVariable(spreadExpression,
+                  collectionType.withDeclaredNullability(Nullability.nullable));
+              parts.add(_createNullAwareGuard(entry.fileOffset, temp,
+                  makeLiteral(entry.fileOffset, []), collectionType));
+            } else {
+              parts.add(spreadExpression);
+            }
+          case NullAwareMapEntry():
+            assert(entry.isKeyNullAware || entry.isValueNullAware);
+            if (currentPart != null) {
+              // Coverage-ignore-block(suite): Not run.
+              parts.add(makeLiteral(node.fileOffset, currentPart));
+              currentPart = null;
+            }
+
+            Expression keyExpression = entry.key;
+            Expression valueExpression = entry.value;
+
+            // Since the desugared map entry may include promotions of the key
+            // or the value expressions, we can't finalize it until the later
+            // stages of desugaring. To assign the promoted expressions as
+            // necessary, we keep track of the map entry node via
+            // [addedMapLiteralEntry].
+            MapLiteralEntry? addedMapLiteralEntry;
+
+            Expression desugaredExpression = new NullLiteral();
+
+            if (entry.isValueNullAware) {
+              VariableDeclaration valueTemp = _createVariable(valueExpression,
+                  node.valueType.withDeclaredNullability(Nullability.nullable));
+              valueExpression = _createNullCheckedVariableGet(valueTemp);
+              Expression defaultValue = makeLiteral(entry.fileOffset, []);
+              addedMapLiteralEntry ??=
+                  new MapLiteralEntry(keyExpression, valueExpression);
+              Expression nullCheckedValue =
+                  makeLiteral(entry.value.fileOffset, [addedMapLiteralEntry]);
+              desugaredExpression = _createNullAwareGuard(
+                  entry.fileOffset, valueTemp, defaultValue, collectionType,
+                  nullCheckedValue: nullCheckedValue);
+            }
+
+            if (entry.isKeyNullAware) {
+              VariableDeclaration keyTemp = _createVariable(entry.key,
+                  node.keyType.withDeclaredNullability(Nullability.nullable));
+              keyExpression = _createNullCheckedVariableGet(keyTemp);
+              Expression defaultValue = makeLiteral(entry.fileOffset, []);
+              Expression nullCheckedKey;
+              if (addedMapLiteralEntry == null) {
+                assert(!entry.isValueNullAware);
+                addedMapLiteralEntry =
+                    new MapLiteralEntry(keyExpression, valueExpression);
+                nullCheckedKey =
+                    makeLiteral(entry.key.fileOffset, [addedMapLiteralEntry]);
+              } else {
+                assert(entry.isValueNullAware);
+                addedMapLiteralEntry.key = keyExpression
+                  ..parent = addedMapLiteralEntry;
+                nullCheckedKey = desugaredExpression;
+              }
+              desugaredExpression = _createNullAwareGuard(
+                  entry.fileOffset, keyTemp, defaultValue, collectionType,
+                  nullCheckedValue: nullCheckedKey);
+            }
+
+            // Since either the key or the value is null-aware,
+            // [desugaredExpression] should be replaced with a null-checking
+            // [Expression].
+            assert(addedMapLiteralEntry != null &&
+                desugaredExpression is! NullLiteral);
+
+            parts.add(desugaredExpression);
+          // Coverage-ignore(suite): Not run.
+          case IfMapEntry():
+            if (currentPart != null) {
+              parts.add(makeLiteral(node.fileOffset, currentPart));
+              currentPart = null;
+            }
+            Expression condition = entry.condition;
+            Expression then = makeLiteral(entry.then.fileOffset, [entry.then]);
+            Expression otherwise = entry.otherwise != null
+                ? makeLiteral(entry.otherwise!.fileOffset, [entry.otherwise!])
+                : makeLiteral(node.fileOffset, []);
+            parts.add(_createConditionalExpression(
+                entry.fileOffset, condition, then, otherwise, collectionType));
+          // Coverage-ignore(suite): Not run.
+          case IfCaseMapEntry():
+          case PatternForMapEntry():
+          case ForMapEntry():
+          case ForInMapEntry():
+            // Rejected earlier.
+            problems.unhandled("${entry.runtimeType}", "_translateConstMap",
+                entry.fileOffset, helper.uri);
         }
-        Expression spreadExpression = entry.expression;
-        if (entry.isNullAware) {
-          VariableDeclaration temp = _createVariable(spreadExpression,
-              collectionType.withDeclaredNullability(Nullability.nullable));
-          parts.add(_createNullAwareGuard(entry.fileOffset, temp,
-              makeLiteral(entry.fileOffset, []), collectionType));
-        } else {
-          parts.add(spreadExpression);
-        }
-      } else if (entry is NullAwareMapEntry) {
-        assert(entry.isKeyNullAware || entry.isValueNullAware);
-        if (currentPart != null) {
-          parts.add(makeLiteral(node.fileOffset, currentPart));
-          currentPart = null;
-        }
-
-        Expression keyExpression = entry.key;
-        Expression valueExpression = entry.value;
-
-        // Since the desugared map entry may include promotions of the key or
-        // the value expressions, we can't finalize it until the later stages of
-        // desugaring. To assign the promoted expressions as necessary, we keep
-        // track of the map entry node via [addedMapLiteralEntry].
-        MapLiteralEntry? addedMapLiteralEntry;
-
-        Expression desugaredExpression = new NullLiteral();
-
-        if (entry.isValueNullAware) {
-          VariableDeclaration valueTemp = _createVariable(valueExpression,
-              node.valueType.withDeclaredNullability(Nullability.nullable));
-          valueExpression = _createNullCheckedVariableGet(valueTemp);
-          Expression defaultValue = makeLiteral(entry.fileOffset, []);
-          addedMapLiteralEntry ??=
-              new MapLiteralEntry(keyExpression, valueExpression);
-          Expression nullCheckedValue =
-              makeLiteral(entry.value.fileOffset, [addedMapLiteralEntry]);
-          desugaredExpression = _createNullAwareGuard(
-              entry.fileOffset, valueTemp, defaultValue, collectionType,
-              nullCheckedValue: nullCheckedValue);
-        }
-
-        if (entry.isKeyNullAware) {
-          VariableDeclaration keyTemp = _createVariable(entry.key,
-              node.keyType.withDeclaredNullability(Nullability.nullable));
-          keyExpression = _createNullCheckedVariableGet(keyTemp);
-          Expression defaultValue = makeLiteral(entry.fileOffset, []);
-          Expression nullCheckedKey;
-          if (addedMapLiteralEntry == null) {
-            assert(!entry.isValueNullAware);
-            addedMapLiteralEntry =
-                new MapLiteralEntry(keyExpression, valueExpression);
-            nullCheckedKey =
-                makeLiteral(entry.key.fileOffset, [addedMapLiteralEntry]);
-          } else {
-            assert(entry.isValueNullAware);
-            addedMapLiteralEntry.key = keyExpression
-              ..parent = addedMapLiteralEntry;
-            nullCheckedKey = desugaredExpression;
-          }
-          desugaredExpression = _createNullAwareGuard(
-              entry.fileOffset, keyTemp, defaultValue, collectionType,
-              nullCheckedValue: nullCheckedKey);
-        }
-
-        // Since either the key or the value is null-aware,
-        // [desugaredExpression] should be replaced with a null-checking
-        // [Expression].
-        assert(addedMapLiteralEntry != null &&
-            desugaredExpression is! NullLiteral);
-
-        parts.add(desugaredExpression);
-      } else if (entry is IfMapEntry) {
-        // Coverage-ignore-block(suite): Not run.
-        if (currentPart != null) {
-          parts.add(makeLiteral(node.fileOffset, currentPart));
-          currentPart = null;
-        }
-        Expression condition = entry.condition;
-        Expression then = makeLiteral(entry.then.fileOffset, [entry.then]);
-        Expression otherwise = entry.otherwise != null
-            ? makeLiteral(entry.otherwise!.fileOffset, [entry.otherwise!])
-            : makeLiteral(node.fileOffset, []);
-        parts.add(_createConditionalExpression(
-            entry.fileOffset, condition, then, otherwise, collectionType));
-      } else if (entry is ForMapEntry || entry is ForInMapEntry) {
-        // Coverage-ignore-block(suite): Not run.
-        // Rejected earlier.
-        problems.unhandled("${entry.runtimeType}", "_translateConstMap",
-            entry.fileOffset, helper.uri);
       } else {
         currentPart ??= <MapLiteralEntry>[];
         currentPart.add(entry);
@@ -4106,7 +4122,6 @@
       return new VariableGet(variable, promotedType)
         ..fileOffset = variable.fileOffset;
     }
-    // Coverage-ignore(suite): Not run.
     return _createVariableGet(variable);
   }
 
@@ -4757,6 +4772,72 @@
     return entry;
   }
 
+  MapLiteralEntry _inferPatternForMapEntry(
+      PatternForMapEntry entry,
+      TreeNode parent,
+      DartType inferredKeyType,
+      DartType inferredValueType,
+      DartType spreadContext,
+      List<DartType> actualTypes,
+      List<DartType> actualTypesForSet,
+      Map<TreeNode, DartType> inferredSpreadTypes,
+      Map<Expression, DartType> inferredConditionTypes,
+      _MapLiteralEntryOffsets offsets) {
+    int? stackBase;
+    assert(checkStackBase(entry, stackBase = stackHeight));
+
+    PatternVariableDeclaration patternVariableDeclaration =
+        entry.patternVariableDeclaration;
+    PatternVariableDeclarationAnalysisResult<DartType, DartType>
+        analysisResult = analyzePatternVariableDeclaration(
+            patternVariableDeclaration,
+            patternVariableDeclaration.pattern,
+            patternVariableDeclaration.initializer,
+            isFinal: patternVariableDeclaration.isFinal);
+    patternVariableDeclaration.matchedValueType =
+        analysisResult.initializerType;
+
+    assert(checkStack(entry, stackBase, [
+      /* pattern = */ ValueKinds.Pattern,
+      /* initializer = */ ValueKinds.Expression,
+    ]));
+
+    Object? rewrite = popRewrite(NullValues.Expression);
+    if (!identical(patternVariableDeclaration.pattern, rewrite)) {
+      // Coverage-ignore-block(suite): Not run.
+      patternVariableDeclaration.pattern = (rewrite as Pattern)
+        ..parent = patternVariableDeclaration;
+    }
+
+    rewrite = popRewrite();
+    if (!identical(patternVariableDeclaration.initializer, rewrite)) {
+      patternVariableDeclaration.initializer = (rewrite as Expression)
+        ..parent = patternVariableDeclaration;
+    }
+
+    List<VariableDeclaration> declaredVariables =
+        patternVariableDeclaration.pattern.declaredVariables;
+    assert(declaredVariables.length == entry.intermediateVariables.length);
+    assert(declaredVariables.length == entry.variables.length);
+    for (int i = 0; i < declaredVariables.length; i++) {
+      DartType type = declaredVariables[i].type;
+      entry.intermediateVariables[i].type = type;
+      entry.variables[i].type = type;
+    }
+
+    return _inferForMapEntryBase(
+        entry,
+        parent,
+        inferredKeyType,
+        inferredValueType,
+        spreadContext,
+        actualTypes,
+        actualTypesForSet,
+        inferredSpreadTypes,
+        inferredConditionTypes,
+        offsets);
+  }
+
   MapLiteralEntry _inferForMapEntry(
       ForMapEntry entry,
       TreeNode parent,
@@ -4768,49 +4849,30 @@
       Map<TreeNode, DartType> inferredSpreadTypes,
       Map<Expression, DartType> inferredConditionTypes,
       _MapLiteralEntryOffsets offsets) {
-    if (entry is PatternForMapEntry) {
-      int? stackBase;
-      assert(checkStackBase(entry, stackBase = stackHeight));
+    return _inferForMapEntryBase(
+        entry,
+        parent,
+        inferredKeyType,
+        inferredValueType,
+        spreadContext,
+        actualTypes,
+        actualTypesForSet,
+        inferredSpreadTypes,
+        inferredConditionTypes,
+        offsets);
+  }
 
-      PatternVariableDeclaration patternVariableDeclaration =
-          entry.patternVariableDeclaration;
-      PatternVariableDeclarationAnalysisResult<DartType, DartType>
-          analysisResult = analyzePatternVariableDeclaration(
-              patternVariableDeclaration,
-              patternVariableDeclaration.pattern,
-              patternVariableDeclaration.initializer,
-              isFinal: patternVariableDeclaration.isFinal);
-      patternVariableDeclaration.matchedValueType =
-          analysisResult.initializerType;
-
-      assert(checkStack(entry, stackBase, [
-        /* pattern = */ ValueKinds.Pattern,
-        /* initializer = */ ValueKinds.Expression,
-      ]));
-
-      Object? rewrite = popRewrite(NullValues.Expression);
-      if (!identical(patternVariableDeclaration.pattern, rewrite)) {
-        // Coverage-ignore-block(suite): Not run.
-        patternVariableDeclaration.pattern = (rewrite as Pattern)
-          ..parent = patternVariableDeclaration;
-      }
-
-      rewrite = popRewrite();
-      if (!identical(patternVariableDeclaration.initializer, rewrite)) {
-        patternVariableDeclaration.initializer = (rewrite as Expression)
-          ..parent = patternVariableDeclaration;
-      }
-
-      List<VariableDeclaration> declaredVariables =
-          patternVariableDeclaration.pattern.declaredVariables;
-      assert(declaredVariables.length == entry.intermediateVariables.length);
-      assert(declaredVariables.length == entry.variables.length);
-      for (int i = 0; i < declaredVariables.length; i++) {
-        DartType type = declaredVariables[i].type;
-        entry.intermediateVariables[i].type = type;
-        entry.variables[i].type = type;
-      }
-    }
+  MapLiteralEntry _inferForMapEntryBase(
+      ForMapEntryBase entry,
+      TreeNode parent,
+      DartType inferredKeyType,
+      DartType inferredValueType,
+      DartType spreadContext,
+      List<DartType> actualTypes,
+      List<DartType> actualTypesForSet,
+      Map<TreeNode, DartType> inferredSpreadTypes,
+      Map<Expression, DartType> inferredConditionTypes,
+      _MapLiteralEntryOffsets offsets) {
     // TODO(johnniwinther): Use _visitStatements instead.
     List<VariableDeclaration>? variables;
     for (int index = 0; index < entry.variables.length; index++) {
@@ -4957,78 +5019,93 @@
       Map<TreeNode, DartType> inferredSpreadTypes,
       Map<Expression, DartType> inferredConditionTypes,
       _MapLiteralEntryOffsets offsets) {
-    if (entry is SpreadMapEntry) {
-      return _inferSpreadMapEntry(
-          entry,
-          parent,
-          inferredKeyType,
-          inferredValueType,
-          spreadContext,
-          actualTypes,
-          actualTypesForSet,
-          inferredSpreadTypes,
-          inferredConditionTypes,
-          offsets);
-    } else if (entry is NullAwareMapEntry) {
-      return _inferNullAwareMapEntry(
-          entry,
-          parent,
-          inferredKeyType,
-          inferredValueType,
-          spreadContext,
-          actualTypes,
-          actualTypesForSet,
-          inferredSpreadTypes,
-          inferredConditionTypes,
-          offsets);
-    } else if (entry is IfMapEntry) {
-      return _inferIfMapEntry(
-          entry,
-          parent,
-          inferredKeyType,
-          inferredValueType,
-          spreadContext,
-          actualTypes,
-          actualTypesForSet,
-          inferredSpreadTypes,
-          inferredConditionTypes,
-          offsets);
-    } else if (entry is IfCaseMapEntry) {
-      return _inferIfCaseMapEntry(
-          entry,
-          parent,
-          inferredKeyType,
-          inferredValueType,
-          spreadContext,
-          actualTypes,
-          actualTypesForSet,
-          inferredSpreadTypes,
-          inferredConditionTypes,
-          offsets);
-    } else if (entry is ForMapEntry) {
-      return _inferForMapEntry(
-          entry,
-          parent,
-          inferredKeyType,
-          inferredValueType,
-          spreadContext,
-          actualTypes,
-          actualTypesForSet,
-          inferredSpreadTypes,
-          inferredConditionTypes,
-          offsets);
-    } else if (entry is ForInMapEntry) {
-      return _inferForInMapEntry(
-          entry,
-          parent,
-          inferredKeyType,
-          inferredValueType,
-          spreadContext,
-          actualTypes,
-          actualTypesForSet,
-          inferredSpreadTypes,
-          inferredConditionTypes,
-          offsets);
+    if (entry is ControlFlowMapEntry) {
+      switch (entry) {
+        case SpreadMapEntry():
+          return _inferSpreadMapEntry(
+              entry,
+              parent,
+              inferredKeyType,
+              inferredValueType,
+              spreadContext,
+              actualTypes,
+              actualTypesForSet,
+              inferredSpreadTypes,
+              inferredConditionTypes,
+              offsets);
+        case NullAwareMapEntry():
+          return _inferNullAwareMapEntry(
+              entry,
+              parent,
+              inferredKeyType,
+              inferredValueType,
+              spreadContext,
+              actualTypes,
+              actualTypesForSet,
+              inferredSpreadTypes,
+              inferredConditionTypes,
+              offsets);
+        case IfMapEntry():
+          return _inferIfMapEntry(
+              entry,
+              parent,
+              inferredKeyType,
+              inferredValueType,
+              spreadContext,
+              actualTypes,
+              actualTypesForSet,
+              inferredSpreadTypes,
+              inferredConditionTypes,
+              offsets);
+        case IfCaseMapEntry():
+          return _inferIfCaseMapEntry(
+              entry,
+              parent,
+              inferredKeyType,
+              inferredValueType,
+              spreadContext,
+              actualTypes,
+              actualTypesForSet,
+              inferredSpreadTypes,
+              inferredConditionTypes,
+              offsets);
+        case ForMapEntry():
+          return _inferForMapEntry(
+              entry,
+              parent,
+              inferredKeyType,
+              inferredValueType,
+              spreadContext,
+              actualTypes,
+              actualTypesForSet,
+              inferredSpreadTypes,
+              inferredConditionTypes,
+              offsets);
+        case PatternForMapEntry():
+          return _inferPatternForMapEntry(
+              entry,
+              parent,
+              inferredKeyType,
+              inferredValueType,
+              spreadContext,
+              actualTypes,
+              actualTypesForSet,
+              inferredSpreadTypes,
+              inferredConditionTypes,
+              offsets);
+        case ForInMapEntry():
+          return _inferForInMapEntry(
+              entry,
+              parent,
+              inferredKeyType,
+              inferredValueType,
+              spreadContext,
+              actualTypes,
+              actualTypesForSet,
+              inferredSpreadTypes,
+              inferredConditionTypes,
+              offsets);
+      }
     } else {
       ExpressionInferenceResult keyResult =
           inferExpression(entry.key, inferredKeyType, isVoidAllowed: true);
@@ -5070,42 +5147,78 @@
           new NullLiteral())
         ..fileOffset = offsets.iterableSpreadOffset!;
     }
-    if (entry is SpreadMapEntry) {
-      DartType? spreadType = inferredSpreadTypes[entry.expression];
-      if (spreadType is DynamicType) {
-        Expression expression = ensureAssignable(
-            coreTypes.mapRawType(entry.isNullAware
-                ? Nullability.nullable
-                : Nullability.nonNullable),
-            spreadType,
-            entry.expression);
-        entry.expression = expression..parent = entry;
+    if (entry is ControlFlowMapEntry) {
+      switch (entry) {
+        case SpreadMapEntry():
+          DartType? spreadType = inferredSpreadTypes[entry.expression];
+          if (spreadType is DynamicType) {
+            Expression expression = ensureAssignable(
+                coreTypes.mapRawType(entry.isNullAware
+                    ? Nullability.nullable
+                    : Nullability.nonNullable),
+                spreadType,
+                entry.expression);
+            entry.expression = expression..parent = entry;
+          }
+        case IfMapEntry():
+          MapLiteralEntry then = checkMapEntry(entry.then, keyType, valueType,
+              inferredSpreadTypes, inferredConditionTypes, offsets);
+          entry.then = then..parent = entry;
+          if (entry.otherwise != null) {
+            MapLiteralEntry otherwise = checkMapEntry(
+                entry.otherwise!,
+                keyType,
+                valueType,
+                inferredSpreadTypes,
+                inferredConditionTypes,
+                offsets);
+            entry.otherwise = otherwise..parent = entry;
+          }
+        case ForMapEntry():
+          if (entry.condition != null) {
+            DartType conditionType = inferredConditionTypes[entry.condition]!;
+            Expression condition = ensureAssignable(
+                coreTypes.boolRawType(Nullability.nonNullable),
+                conditionType,
+                entry.condition!);
+            entry.condition = condition..parent = entry;
+          }
+          MapLiteralEntry body = checkMapEntry(entry.body, keyType, valueType,
+              inferredSpreadTypes, inferredConditionTypes, offsets);
+          entry.body = body..parent = entry;
+        case PatternForMapEntry():
+          if (entry.condition != null) {
+            DartType conditionType = inferredConditionTypes[entry.condition]!;
+            Expression condition = ensureAssignable(
+                coreTypes.boolRawType(Nullability.nonNullable),
+                conditionType,
+                entry.condition!);
+            entry.condition = condition..parent = entry;
+          }
+          MapLiteralEntry body = checkMapEntry(entry.body, keyType, valueType,
+              inferredSpreadTypes, inferredConditionTypes, offsets);
+          entry.body = body..parent = entry;
+        case ForInMapEntry():
+          MapLiteralEntry body = checkMapEntry(entry.body, keyType, valueType,
+              inferredSpreadTypes, inferredConditionTypes, offsets);
+          entry.body = body..parent = entry;
+        case IfCaseMapEntry():
+          MapLiteralEntry then = checkMapEntry(entry.then, keyType, valueType,
+              inferredSpreadTypes, inferredConditionTypes, offsets);
+          entry.then = then..parent = entry;
+          if (entry.otherwise != null) {
+            MapLiteralEntry otherwise = checkMapEntry(
+                entry.otherwise!,
+                keyType,
+                valueType,
+                inferredSpreadTypes,
+                inferredConditionTypes,
+                offsets);
+            entry.otherwise = otherwise..parent = entry;
+          }
+        case NullAwareMapEntry():
+        // Do nothing.  Assignability checks are done during type inference.
       }
-    } else if (entry is IfMapEntry) {
-      MapLiteralEntry then = checkMapEntry(entry.then, keyType, valueType,
-          inferredSpreadTypes, inferredConditionTypes, offsets);
-      entry.then = then..parent = entry;
-      if (entry.otherwise != null) {
-        MapLiteralEntry otherwise = checkMapEntry(entry.otherwise!, keyType,
-            valueType, inferredSpreadTypes, inferredConditionTypes, offsets);
-        entry.otherwise = otherwise..parent = entry;
-      }
-    } else if (entry is ForMapEntry) {
-      if (entry.condition != null) {
-        DartType conditionType = inferredConditionTypes[entry.condition]!;
-        Expression condition = ensureAssignable(
-            coreTypes.boolRawType(Nullability.nonNullable),
-            conditionType,
-            entry.condition!);
-        entry.condition = condition..parent = entry;
-      }
-      MapLiteralEntry body = checkMapEntry(entry.body, keyType, valueType,
-          inferredSpreadTypes, inferredConditionTypes, offsets);
-      entry.body = body..parent = entry;
-    } else if (entry is ForInMapEntry) {
-      MapLiteralEntry body = checkMapEntry(entry.body, keyType, valueType,
-          inferredSpreadTypes, inferredConditionTypes, offsets);
-      entry.body = body..parent = entry;
     } else {
       // Do nothing.  Assignability checks are done during type inference.
     }
diff --git a/pkg/front_end/lib/src/type_inference/inference_visitor_base.dart b/pkg/front_end/lib/src/type_inference/inference_visitor_base.dart
index a76a137..de33a56 100644
--- a/pkg/front_end/lib/src/type_inference/inference_visitor_base.dart
+++ b/pkg/front_end/lib/src/type_inference/inference_visitor_base.dart
@@ -3448,6 +3448,7 @@
                   .withDeclaredNullability(Nullability.nullable)
             ], functionType.returnType, functionType.declaredNullability));
           }
+        // Coverage-ignore(suite): Not run.
         case InvocationTargetDynamicType():
         case InvocationTargetNeverType():
         case InvocationTargetInvalidType():
diff --git a/pkg/front_end/lib/src/type_inference/object_access_target.dart b/pkg/front_end/lib/src/type_inference/object_access_target.dart
index fceb24b..9c2edde 100644
--- a/pkg/front_end/lib/src/type_inference/object_access_target.dart
+++ b/pkg/front_end/lib/src/type_inference/object_access_target.dart
@@ -197,52 +197,62 @@
   const InvocationTargetNonFunctionType();
 
   @override
+  // Coverage-ignore(suite): Not run.
   FunctionType get equalsFunctionType {
     return _oneParameterFunctionApproximation;
   }
 
   @override
+  // Coverage-ignore(suite): Not run.
   FunctionType get sublistFunctionType {
     return _oneParameterFunctionApproximation;
   }
 
   @override
+  // Coverage-ignore(suite): Not run.
   FunctionType get minusFunctionType {
     return _oneParameterFunctionApproximation;
   }
 
   @override
+  // Coverage-ignore(suite): Not run.
   FunctionType get indexGetFunctionType {
     return _oneParameterFunctionApproximation;
   }
 
   @override
+  // Coverage-ignore(suite): Not run.
   FunctionType get greaterThanOrEqualsFunctionType {
     return _oneParameterFunctionApproximation;
   }
 
   @override
+  // Coverage-ignore(suite): Not run.
   FunctionType get lessThanOrEqualsFunctionType {
     return _oneParameterFunctionApproximation;
   }
 
   @override
+  // Coverage-ignore(suite): Not run.
   FunctionType get relationalFunctionType {
     return _oneParameterFunctionApproximation;
   }
 
   @override
+  // Coverage-ignore(suite): Not run.
   FunctionType get containsKeyFunctionType {
     return _oneParameterFunctionApproximation;
   }
 
   @override
+  // Coverage-ignore(suite): Not run.
   FunctionType get indexSetFunctionType {
     return new FunctionType(
         [const DynamicType()], returnType, Nullability.nonNullable);
   }
 
   @override
+  // Coverage-ignore(suite): Not run.
   FunctionType get binaryOperationFunctionType {
     return _oneParameterFunctionApproximation;
   }
@@ -254,6 +264,7 @@
     return FunctionAccessKind.Function;
   }
 
+  // Coverage-ignore(suite): Not run.
   FunctionType get _oneParameterFunctionApproximation {
     return new FunctionType(
         [const DynamicType()], returnType, Nullability.nonNullable);
@@ -540,6 +551,7 @@
         calleeType is DynamicType) {
       return const InvocationTargetDynamicType();
     } else if (calleeType is NeverType) {
+      // Coverage-ignore-block(suite): Not run.
       // TODO(cstefantsova): Should we insert the nullability assert somewhere
       // earlier?
       assert(calleeType.nullability == Nullability.nonNullable);
@@ -769,6 +781,7 @@
         } else {
           return const DynamicType();
         }
+      // Coverage-ignore(suite): Not run.
       case InvocationTargetDynamicType():
       case InvocationTargetInvalidType():
       case InvocationTargetNeverType():
@@ -790,6 +803,7 @@
         } else {
           return const DynamicType();
         }
+      // Coverage-ignore(suite): Not run.
       case InvocationTargetDynamicType():
       case InvocationTargetInvalidType():
       case InvocationTargetNeverType():
@@ -816,6 +830,7 @@
         } else {
           return const DynamicType();
         }
+      // Coverage-ignore(suite): Not run.
       case InvocationTargetDynamicType():
       case InvocationTargetInvalidType():
       case InvocationTargetNeverType():
diff --git a/pkg/front_end/lib/src/type_inference/type_inference_engine.dart b/pkg/front_end/lib/src/type_inference/type_inference_engine.dart
index 7141a8a..17420c0 100644
--- a/pkg/front_end/lib/src/type_inference/type_inference_engine.dart
+++ b/pkg/front_end/lib/src/type_inference/type_inference_engine.dart
@@ -985,6 +985,7 @@
   }
 
   @override
+  // Coverage-ignore(suite): Not run.
   NullabilitySuffix typeSchemaNullabilitySuffix(DartType typeSchema) {
     return typeSchema.nullabilitySuffix;
   }
diff --git a/pkg/front_end/lib/src/type_inference/type_inferrer.dart b/pkg/front_end/lib/src/type_inference/type_inferrer.dart
index 8decdbf..207282b 100644
--- a/pkg/front_end/lib/src/type_inference/type_inferrer.dart
+++ b/pkg/front_end/lib/src/type_inference/type_inferrer.dart
@@ -218,7 +218,6 @@
     List<NamedExpression> namedArguments = <NamedExpression>[];
     for (VariableDeclaration parameter
         in redirectingFactoryFunction.namedParameters) {
-      // Coverage-ignore-block(suite): Not run.
       flowAnalysis.declare(parameter, parameter.type, initialized: true);
       namedArguments.add(new NamedExpression(parameter.name!,
           new VariableGetImpl(parameter, forNullGuardedAccess: false)));
diff --git a/pkg/front_end/test/coverage_suite.dart b/pkg/front_end/test/coverage_suite.dart
index efdc435..fe0f726 100644
--- a/pkg/front_end/test/coverage_suite.dart
+++ b/pkg/front_end/test/coverage_suite.dart
@@ -64,12 +64,13 @@
   print("Run finished.");
 
   Map<Uri, coverageMerger.CoverageInfo>? coverageData =
-      coverageMerger.mergeFromDirUri(
+      await coverageMerger.mergeFromDirUri(
     Uri.base.resolve(".dart_tool/package_config.json"),
     coverageTmpDir.uri,
     silent: true,
     extraCoverageIgnores: ["coverage-ignore(suite):"],
     extraCoverageBlockIgnores: ["coverage-ignore-block(suite):"],
+    addAndRemoveCommentsInFiles: options.addAndRemoveCommentsInFiles,
   );
   if (coverageData == null) throw "Failure in coverage.";
 
@@ -167,7 +168,9 @@
           sb.write("\n\n   Or update the expectations directly via");
           sb.write(
               "\n\n   dart$extraFlags pkg/front_end/test/coverage_suite.dart "
-              "--tasks=${options.numberOfWorkers} --updateExpectations");
+              "--tasks=${options.numberOfWorkers} "
+              "--add-and-remove-comments "
+              "--update-expectations");
         }
       }
       addResult(coverageEntry.key.toString(), pass,
@@ -229,6 +232,7 @@
   final bool verbose;
   final bool debug;
   final bool updateExpectations;
+  final bool addAndRemoveCommentsInFiles;
   final Uri outputDirectory;
   final int numberOfWorkers;
 
@@ -237,6 +241,7 @@
     this.verbose,
     this.debug,
     this.updateExpectations,
+    this.addAndRemoveCommentsInFiles,
     this.outputDirectory, {
     required this.numberOfWorkers,
   });
@@ -255,8 +260,12 @@
           abbr: "j",
           help: "The number of parallel tasks to run.",
           defaultsTo: "${getDefaultThreads()}")
-      ..addFlag("updateExpectations",
+      ..addFlag("update-expectations",
           help: "update expectations", defaultsTo: false)
+      ..addFlag("add-and-remove-comments",
+          help: "Automatically remove old and then "
+              "re-add ignore comments in files",
+          defaultsTo: false)
       // These are not used but are here for compatibility with the test system.
       ..addOption("shards", help: "(Ignored) Number of shards", defaultsTo: "1")
       ..addOption("shard",
@@ -273,7 +282,8 @@
     if (tasks == null || tasks < 1) {
       throw "--tasks (-j) has to be an integer >= 1";
     }
-    bool updateExpectations = parsedOptions["updateExpectations"];
+    bool updateExpectations = parsedOptions["update-expectations"];
+    bool addAndRemoveCommentsInFiles = parsedOptions["add-and-remove-comments"];
 
     if (verbose) {
       print("NOTE: Created with options\n  "
@@ -282,7 +292,8 @@
           "debug = ${debug},\n  "
           "${outputDirectory},\n  "
           "numberOfWorkers: ${tasks},\n  "
-          "updateExpectations = ${updateExpectations}");
+          "updateExpectations = ${updateExpectations}\n  "
+          "addAndRemoveCommentsInFiles = ${addAndRemoveCommentsInFiles}");
     }
 
     return Options(
@@ -290,6 +301,7 @@
       verbose,
       debug,
       updateExpectations,
+      addAndRemoveCommentsInFiles,
       outputDirectory,
       numberOfWorkers: tasks,
     );
diff --git a/pkg/front_end/test/coverage_suite_expected.dart b/pkg/front_end/test/coverage_suite_expected.dart
index 9e7a7a3..667bdc0 100644
--- a/pkg/front_end/test/coverage_suite_expected.dart
+++ b/pkg/front_end/test/coverage_suite_expected.dart
@@ -168,10 +168,10 @@
     hitCount: 25,
     missCount: 0,
   ),
-  // 86.76470588235294%.
+  // 100.0%.
   "package:front_end/src/base/local_scope.dart": (
     hitCount: 59,
-    missCount: 9,
+    missCount: 0,
   ),
   // 100.0%.
   "package:front_end/src/base/messages.dart": (
@@ -183,10 +183,10 @@
     hitCount: 29,
     missCount: 0,
   ),
-  // 97.87234042553192%.
+  // 100.0%.
   "package:front_end/src/base/name_space.dart": (
     hitCount: 138,
-    missCount: 3,
+    missCount: 0,
   ),
   // 100.0%.
   "package:front_end/src/base/operator.dart": (
@@ -203,10 +203,10 @@
     hitCount: 246,
     missCount: 0,
   ),
-  // 97.94117647058823%.
+  // 100.0%.
   "package:front_end/src/base/scope.dart": (
     hitCount: 666,
-    missCount: 14,
+    missCount: 0,
   ),
   // 100.0%.
   "package:front_end/src/base/ticker.dart": (
@@ -418,25 +418,25 @@
     hitCount: 16,
     missCount: 0,
   ),
-  // 98.85714285714286%.
+  // 100.0%.
   "package:front_end/src/dill/dill_class_builder.dart": (
     hitCount: 173,
-    missCount: 2,
+    missCount: 0,
   ),
-  // 97.5%.
+  // 100.0%.
   "package:front_end/src/dill/dill_extension_builder.dart": (
     hitCount: 78,
-    missCount: 2,
+    missCount: 0,
   ),
   // 100.0%.
   "package:front_end/src/dill/dill_extension_member_builder.dart": (
     hitCount: 71,
     missCount: 0,
   ),
-  // 98.68421052631578%.
+  // 100.0%.
   "package:front_end/src/dill/dill_extension_type_declaration_builder.dart": (
     hitCount: 150,
-    missCount: 2,
+    missCount: 0,
   ),
   // 100.0%.
   "package:front_end/src/dill/dill_extension_type_member_builder.dart": (
@@ -478,20 +478,20 @@
     hitCount: 0,
     missCount: 0,
   ),
-  // 99.14393499709809%.
+  // 100.0%.
   "package:front_end/src/kernel/body_builder.dart": (
-    hitCount: 6833,
-    missCount: 59,
+    hitCount: 6831,
+    missCount: 0,
   ),
-  // 98.26086956521739%.
+  // 100.0%.
   "package:front_end/src/kernel/body_builder_context.dart": (
     hitCount: 339,
-    missCount: 6,
+    missCount: 0,
   ),
-  // 81.57894736842105%.
+  // 100.0%.
   "package:front_end/src/kernel/collections.dart": (
-    hitCount: 310,
-    missCount: 70,
+    hitCount: 326,
+    missCount: 0,
   ),
   // 100.0%.
   "package:front_end/src/kernel/combined_member_signature.dart": (
@@ -598,30 +598,30 @@
     hitCount: 1,
     missCount: 0,
   ),
-  // 99.12126537785588%.
+  // 100.0%.
   "package:front_end/src/kernel/internal_ast.dart": (
-    hitCount: 564,
-    missCount: 5,
+    hitCount: 554,
+    missCount: 0,
   ),
   // 100.0%.
   "package:front_end/src/kernel/invalid_type.dart": (
     hitCount: 43,
     missCount: 0,
   ),
-  // 92.85714285714286%.
+  // 100.0%.
   "package:front_end/src/kernel/kernel_constants.dart": (
     hitCount: 13,
-    missCount: 1,
+    missCount: 0,
   ),
   // 100.0%.
   "package:front_end/src/kernel/kernel_helper.dart": (
     hitCount: 285,
     missCount: 0,
   ),
-  // 99.90566037735849%.
+  // 100.0%.
   "package:front_end/src/kernel/kernel_target.dart": (
     hitCount: 1059,
-    missCount: 1,
+    missCount: 0,
   ),
   // 100.0%.
   "package:front_end/src/kernel/kernel_variable_builder.dart": (
@@ -683,10 +683,10 @@
     hitCount: 16,
     missCount: 0,
   ),
-  // 88.88888888888889%.
+  // 100.0%.
   "package:front_end/src/kernel/try_constant_evaluator.dart": (
     hitCount: 16,
-    missCount: 2,
+    missCount: 0,
   ),
   // 100.0%.
   "package:front_end/src/kernel/type_algorithms.dart": (
@@ -708,10 +708,10 @@
     hitCount: 21,
     missCount: 0,
   ),
-  // 60.0%.
+  // 100.0%.
   "package:front_end/src/kernel/wildcard_lowering.dart": (
     hitCount: 9,
-    missCount: 6,
+    missCount: 0,
   ),
   // 100.0%.
   "package:front_end/src/kernel_generator_impl.dart": (
@@ -723,10 +723,10 @@
     hitCount: 0,
     missCount: 0,
   ),
-  // 0.0%.
+  // 100.0%.
   "package:front_end/src/macros/macro_injected_impl.dart": (
     hitCount: 0,
-    missCount: 1,
+    missCount: 0,
   ),
   // 100.0%.
   "package:front_end/src/macros/macro_serializer.dart": (
@@ -793,30 +793,30 @@
     hitCount: 1167,
     missCount: 0,
   ),
-  // 98.13664596273291%.
+  // 100.0%.
   "package:front_end/src/source/source_builder_mixins.dart": (
     hitCount: 158,
-    missCount: 3,
+    missCount: 0,
   ),
-  // 99.7584541062802%.
+  // 100.0%.
   "package:front_end/src/source/source_class_builder.dart": (
     hitCount: 1239,
-    missCount: 3,
+    missCount: 0,
   ),
-  // 99.46308724832214%.
+  // 100.0%.
   "package:front_end/src/source/source_compilation_unit.dart": (
     hitCount: 741,
-    missCount: 4,
+    missCount: 0,
   ),
-  // 98.31649831649831%.
+  // 100.0%.
   "package:front_end/src/source/source_constructor_builder.dart": (
     hitCount: 876,
-    missCount: 15,
+    missCount: 0,
   ),
-  // 99.81024667931689%.
+  // 100.0%.
   "package:front_end/src/source/source_enum_builder.dart": (
     hitCount: 526,
-    missCount: 1,
+    missCount: 0,
   ),
   // 100.0%.
   "package:front_end/src/source/source_extension_builder.dart": (
@@ -844,15 +844,15 @@
     hitCount: 302,
     missCount: 0,
   ),
-  // 99.85925404644617%.
+  // 100.0%.
   "package:front_end/src/source/source_library_builder.dart": (
     hitCount: 1419,
-    missCount: 2,
+    missCount: 0,
   ),
-  // 99.89253089736701%.
+  // 100.0%.
   "package:front_end/src/source/source_loader.dart": (
     hitCount: 1859,
-    missCount: 2,
+    missCount: 0,
   ),
   // 100.0%.
   "package:front_end/src/source/source_member_builder.dart": (
@@ -864,20 +864,20 @@
     hitCount: 518,
     missCount: 0,
   ),
-  // 99.10714285714286%.
+  // 100.0%.
   "package:front_end/src/source/source_type_alias_builder.dart": (
     hitCount: 333,
-    missCount: 3,
+    missCount: 0,
   ),
   // 100.0%.
   "package:front_end/src/source/stack_listener_impl.dart": (
     hitCount: 20,
     missCount: 0,
   ),
-  // 99.00497512437812%.
+  // 100.0%.
   "package:front_end/src/source/type_parameter_scope_builder.dart": (
     hitCount: 199,
-    missCount: 2,
+    missCount: 0,
   ),
   // 100.0%.
   "package:front_end/src/type_inference/closure_context.dart": (
@@ -909,15 +909,15 @@
     hitCount: 166,
     missCount: 0,
   ),
-  // 99.51564828614009%.
+  // 100.0%.
   "package:front_end/src/type_inference/inference_visitor.dart": (
-    hitCount: 8013,
-    missCount: 39,
+    hitCount: 8044,
+    missCount: 0,
   ),
-  // 99.875%.
+  // 100.0%.
   "package:front_end/src/type_inference/inference_visitor_base.dart": (
     hitCount: 2397,
-    missCount: 3,
+    missCount: 0,
   ),
   // 100.0%.
   "package:front_end/src/type_inference/matching_cache.dart": (
@@ -929,10 +929,10 @@
     hitCount: 519,
     missCount: 0,
   ),
-  // 92.11409395973155%.
+  // 100.0%.
   "package:front_end/src/type_inference/object_access_target.dart": (
     hitCount: 549,
-    missCount: 47,
+    missCount: 0,
   ),
   // 100.0%.
   "package:front_end/src/type_inference/shared_type_analyzer.dart": (
@@ -954,10 +954,10 @@
     hitCount: 19,
     missCount: 0,
   ),
-  // 99.5959595959596%.
+  // 100.0%.
   "package:front_end/src/type_inference/type_inference_engine.dart": (
     hitCount: 493,
-    missCount: 2,
+    missCount: 0,
   ),
   // 100.0%.
   "package:front_end/src/type_inference/type_inferrer.dart": (
diff --git a/pkg/front_end/test/dartdoc_test_test.dart b/pkg/front_end/test/dartdoc_test_test.dart
index 978514b..339898e 100644
--- a/pkg/front_end/test/dartdoc_test_test.dart
+++ b/pkg/front_end/test/dartdoc_test_test.dart
@@ -46,7 +46,7 @@
   memoryFileSystem.entityForUri(test1).writeAsStringSync(test);
   expect(await dartDocTest.process(test1), expected);
 
-// Mixed good/bad.
+  // Mixed good/bad.
   Uri test2 = new Uri(scheme: "darttest", path: "/test2.dart");
   test = """
 // DartDocTest(1+1, 3)
@@ -72,7 +72,7 @@
   memoryFileSystem.entityForUri(test2).writeAsStringSync(test);
   expect(await dartDocTest.process(test2), expected);
 
-// Good case using await.
+  // Good case using await.
   Uri test3 = new Uri(scheme: "darttest", path: "/test3.dart");
   test = """
 // DartDocTest(await _internal(), 42)
@@ -120,7 +120,7 @@
   return;
 }
 """;
-  tests = extractTests(test);
+  tests = extractTests(test, test5);
   expect(tests.length, 2);
   expected = [
     new impl.TestResult(null, impl.TestOutcome.CompilationError)
@@ -145,7 +145,7 @@
   return "hello";
 }
 """;
-  tests = extractTests(test);
+  tests = extractTests(test, test6);
   expect(tests.length, 2);
   expected = [
     new impl.TestResult(tests[0], impl.TestOutcome.Crash)
@@ -183,8 +183,12 @@
   expect(extractTests(""), <impl.Test>[]);
 
   // One test.
-  expect(extractTests("// DartDocTest(1+1, 2)"), <impl.Test>[
-    new impl.ExpectTest("1+1", "2"),
+  expect(extractTests("""
+    // not a test comment
+    void foo_bar_long_name() {}
+
+    // DartDocTest(1+1, 2)"""), <impl.Test>[
+    new impl.ExpectTest("1+1", "2", "darttest:/foo.dart:4:20"),
   ]);
 
   // Two tests.
@@ -192,8 +196,8 @@
 // DartDocTest(1+1, 2)
 // DartDocTest(2+40, 42)
 """), <impl.Test>[
-    new impl.ExpectTest("1+1", "2"),
-    new impl.ExpectTest("2+40", "42"),
+    new impl.ExpectTest("1+1", "2", "darttest:/foo.dart:1:16"),
+    new impl.ExpectTest("2+40", "42", "darttest:/foo.dart:2:16"),
   ]);
 
   // Two valid tests. Four invalid ones.
@@ -205,26 +209,48 @@
 // DartDocTest(2+40, 42)
 // DartDocTest(2+40, 42+)
 """), <impl.Test>[
-    new impl.ExpectTest("1+1", "2"),
+    new impl.ExpectTest(
+      "1+1",
+      "2",
+      "darttest:/foo.dart:1:16",
+    ),
     new impl.TestParseError(
-        """darttest:/foo.dart:2:20: Expected ',' before this.
+      """darttest:/foo.dart:2:20: Expected ',' before this.
 // DartDocTest(2+40; 42]
-                   ^""", 42),
-    new impl.TestParseError("""Parse error(s):
+                   ^""",
+      42,
+      "darttest:/foo.dart:2:20",
+    ),
+    new impl.TestParseError(
+      """Parse error(s):
 
 darttest:/foo.dart:3:21: Expected an identifier, but got ','.
 // DartDocTest(2+40+, 42]
-                    ^""", 68),
+                    ^""",
+      68,
+      "darttest:/foo.dart:3:21",
+    ),
     new impl.TestParseError(
-        """darttest:/foo.dart:4:24: Expected ')' before this.
+      """darttest:/foo.dart:4:24: Expected ')' before this.
 // DartDocTest(2+40, 42]
-                       ^""", 97),
-    new impl.ExpectTest("2+40", "42"),
-    new impl.TestParseError("""Parse error(s):
+                       ^""",
+      97,
+      "darttest:/foo.dart:4:24",
+    ),
+    new impl.ExpectTest(
+      "2+40",
+      "42",
+      "darttest:/foo.dart:5:16",
+    ),
+    new impl.TestParseError(
+      """Parse error(s):
 
 darttest:/foo.dart:6:25: Expected an identifier, but got ')'.
 // DartDocTest(2+40, 42+)
-                        ^""", 148),
+                        ^""",
+      148,
+      "darttest:/foo.dart:6:25",
+    ),
   ]);
 
   // Two tests in block comments with back-ticks around tests.
@@ -240,8 +266,8 @@
  * ```
  */
 """), <impl.Test>[
-    new impl.ExpectTest("1+1", "2"),
-    new impl.ExpectTest("2+40", "42"),
+    new impl.ExpectTest("1+1", "2", "darttest:/foo.dart:4:16"),
+    new impl.ExpectTest("2+40", "42", "darttest:/foo.dart:8:16"),
   ]);
 
   // Two tests --- include linebreaks.
@@ -256,9 +282,9 @@
   42)
  */
 """), <impl.Test>[
-    new impl.ExpectTest("1+1", "2"),
+    new impl.ExpectTest("1+1", "2", "darttest:/foo.dart:3:15"),
     // The linebreak etc here is not stripped (at the moment at least)
-    new impl.ExpectTest("2+\n  40", "42"),
+    new impl.ExpectTest("2+\n  40", "42", "darttest:/foo.dart:6:15"),
   ]);
 
   // Two tests --- with parens and commas as string...
@@ -267,15 +293,15 @@
 // and so is this:
 // DartDocTest(",)", ",)")
 """), <impl.Test>[
-    new impl.ExpectTest('"("', '"("'),
-    new impl.ExpectTest('",)"', '",)"'),
+    new impl.ExpectTest('"("', '"("', "darttest:/foo.dart:1:16"),
+    new impl.ExpectTest('",)"', '",)"', "darttest:/foo.dart:3:16"),
   ]);
 
   // Await expression.
   expect(extractTests("""
 // DartDocTest(await foo(), 42)
 """), <impl.Test>[
-    new impl.ExpectTest('await foo()', '42'),
+    new impl.ExpectTest('await foo()', '42', "darttest:/foo.dart:1:16"),
   ]);
 }
 
diff --git a/pkg/front_end/test/run_all_coverage.dart b/pkg/front_end/test/run_all_coverage.dart
index 5dea1b2..8748b13 100644
--- a/pkg/front_end/test/run_all_coverage.dart
+++ b/pkg/front_end/test/run_all_coverage.dart
@@ -112,7 +112,7 @@
   // force compile everything and that the remaining stuff is (mostly) mixins
   // and const classes etc that shouldn't (necessarily) be compiled but is
   // potentially covered in other ways.
-  coverageMerger.mergeFromDirUri(
+  await coverageMerger.mergeFromDirUri(
     repoDirUri.resolve(".dart_tool/package_config.json"),
     coverageTmpDir.uri,
     silent: false,
diff --git a/pkg/front_end/test/spell_checking_list_tests.txt b/pkg/front_end/test/spell_checking_list_tests.txt
index 23cfea6..cc95db5 100644
--- a/pkg/front_end/test/spell_checking_list_tests.txt
+++ b/pkg/front_end/test/spell_checking_list_tests.txt
@@ -868,6 +868,7 @@
 verbatim
 versioned
 vf
+virtually
 visualization
 vp
 vt
diff --git a/pkg/front_end/testcases/patterns/coersion_in_if_case.dart b/pkg/front_end/testcases/patterns/coersion_in_if_case.dart
new file mode 100644
index 0000000..fd4e966
--- /dev/null
+++ b/pkg/front_end/testcases/patterns/coersion_in_if_case.dart
@@ -0,0 +1,27 @@
+// 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.
+
+test(dynamic x, dynamic list) {
+  return {1: 1, if (list case [int _]) ...x else ...x};
+}
+
+main() {
+  test({0: 0}, [0]);
+  expectThrows<TypeError>(() {test(null, [0]);});
+}
+
+expectThrows<Exception>(void Function() f) {
+  String? message;
+  try {
+    f();
+    message = "Expected the function to throw an exception, but it didn't.";
+  } on Exception catch (_) {
+    // Ok.
+  } on dynamic catch (e) {
+    message = "Expected the function to throw an exception of type '${Exception}', but got '${e.runtimeType}'.";
+  }
+  if (message != null) {
+    throw message;
+  }
+}
diff --git a/pkg/front_end/testcases/patterns/coersion_in_if_case.dart.strong.expect b/pkg/front_end/testcases/patterns/coersion_in_if_case.dart.strong.expect
new file mode 100644
index 0000000..a4d3c35
--- /dev/null
+++ b/pkg/front_end/testcases/patterns/coersion_in_if_case.dart.strong.expect
@@ -0,0 +1,42 @@
+library;
+import self as self;
+import "dart:core" as core;
+
+static method test(dynamic x, dynamic list) → dynamic {
+  return block {
+    final core::Map<dynamic, dynamic> #t1 = <dynamic, dynamic>{};
+    #t1.{core::Map::[]=}{Invariant}(1, 1){(dynamic, dynamic) → void};
+    {
+      final synthesized dynamic #0#0 = list;
+      if(#0#0 is core::List<dynamic> && #0#0{core::List<dynamic>}.{core::List::length}{core::int} =={core::num::==}{(core::Object) → core::bool} #C1 && #0#0{core::List<dynamic>}.{core::List::[]}(0){(core::int) → dynamic} is core::int)
+        #t1.{core::Map::addAll}{Invariant}(x as{TypeError,ForDynamic} core::Map<dynamic, dynamic>){(core::Map<dynamic, dynamic>) → void};
+      else
+        #t1.{core::Map::addAll}{Invariant}(x as{TypeError,ForDynamic} core::Map<dynamic, dynamic>){(core::Map<dynamic, dynamic>) → void};
+    }
+  } =>#t1;
+}
+static method main() → dynamic {
+  self::test(<core::int, core::int>{0: 0}, <core::int>[0]);
+  self::expectThrows<core::TypeError>(() → void {
+    self::test(null, <core::int>[0]);
+  });
+}
+static method expectThrows<Exception extends core::Object? = dynamic>(() → void f) → dynamic {
+  core::String? message;
+  try {
+    f(){() → void};
+    message = "Expected the function to throw an exception, but it didn't.";
+  }
+  on self::expectThrows::Exception% catch(final self::expectThrows::Exception% _) {
+  }
+  on dynamic catch(final dynamic e) {
+    message = "Expected the function to throw an exception of type '${self::expectThrows::Exception%}', but got '${e.{core::Object::runtimeType}{<object>}.{core::Type}}'.";
+  }
+  if(!(message == null)) {
+    throw message{core::String};
+  }
+}
+
+constants  {
+  #C1 = 1
+}
diff --git a/pkg/front_end/testcases/patterns/coersion_in_if_case.dart.strong.modular.expect b/pkg/front_end/testcases/patterns/coersion_in_if_case.dart.strong.modular.expect
new file mode 100644
index 0000000..a4d3c35
--- /dev/null
+++ b/pkg/front_end/testcases/patterns/coersion_in_if_case.dart.strong.modular.expect
@@ -0,0 +1,42 @@
+library;
+import self as self;
+import "dart:core" as core;
+
+static method test(dynamic x, dynamic list) → dynamic {
+  return block {
+    final core::Map<dynamic, dynamic> #t1 = <dynamic, dynamic>{};
+    #t1.{core::Map::[]=}{Invariant}(1, 1){(dynamic, dynamic) → void};
+    {
+      final synthesized dynamic #0#0 = list;
+      if(#0#0 is core::List<dynamic> && #0#0{core::List<dynamic>}.{core::List::length}{core::int} =={core::num::==}{(core::Object) → core::bool} #C1 && #0#0{core::List<dynamic>}.{core::List::[]}(0){(core::int) → dynamic} is core::int)
+        #t1.{core::Map::addAll}{Invariant}(x as{TypeError,ForDynamic} core::Map<dynamic, dynamic>){(core::Map<dynamic, dynamic>) → void};
+      else
+        #t1.{core::Map::addAll}{Invariant}(x as{TypeError,ForDynamic} core::Map<dynamic, dynamic>){(core::Map<dynamic, dynamic>) → void};
+    }
+  } =>#t1;
+}
+static method main() → dynamic {
+  self::test(<core::int, core::int>{0: 0}, <core::int>[0]);
+  self::expectThrows<core::TypeError>(() → void {
+    self::test(null, <core::int>[0]);
+  });
+}
+static method expectThrows<Exception extends core::Object? = dynamic>(() → void f) → dynamic {
+  core::String? message;
+  try {
+    f(){() → void};
+    message = "Expected the function to throw an exception, but it didn't.";
+  }
+  on self::expectThrows::Exception% catch(final self::expectThrows::Exception% _) {
+  }
+  on dynamic catch(final dynamic e) {
+    message = "Expected the function to throw an exception of type '${self::expectThrows::Exception%}', but got '${e.{core::Object::runtimeType}{<object>}.{core::Type}}'.";
+  }
+  if(!(message == null)) {
+    throw message{core::String};
+  }
+}
+
+constants  {
+  #C1 = 1
+}
diff --git a/pkg/front_end/testcases/patterns/coersion_in_if_case.dart.strong.outline.expect b/pkg/front_end/testcases/patterns/coersion_in_if_case.dart.strong.outline.expect
new file mode 100644
index 0000000..47a0d8c
--- /dev/null
+++ b/pkg/front_end/testcases/patterns/coersion_in_if_case.dart.strong.outline.expect
@@ -0,0 +1,10 @@
+library;
+import self as self;
+import "dart:core" as core;
+
+static method test(dynamic x, dynamic list) → dynamic
+  ;
+static method main() → dynamic
+  ;
+static method expectThrows<Exception extends core::Object? = dynamic>(() → void f) → dynamic
+  ;
diff --git a/pkg/front_end/testcases/patterns/coersion_in_if_case.dart.strong.transformed.expect b/pkg/front_end/testcases/patterns/coersion_in_if_case.dart.strong.transformed.expect
new file mode 100644
index 0000000..6a23afa
--- /dev/null
+++ b/pkg/front_end/testcases/patterns/coersion_in_if_case.dart.strong.transformed.expect
@@ -0,0 +1,42 @@
+library;
+import self as self;
+import "dart:core" as core;
+
+static method test(dynamic x, dynamic list) → dynamic {
+  return block {
+    final core::Map<dynamic, dynamic> #t1 = <dynamic, dynamic>{};
+    #t1.{core::Map::[]=}{Invariant}(1, 1){(dynamic, dynamic) → void};
+    {
+      final synthesized dynamic #0#0 = list;
+      if(#0#0 is core::List<dynamic> && #0#0{core::List<dynamic>}.{core::List::length}{core::int} =={core::num::==}{(core::Object) → core::bool} #C1 && #0#0{core::List<dynamic>}.{core::List::[]}(0){(core::int) → dynamic} is core::int)
+        #t1.{core::Map::addAll}{Invariant}(x as{TypeError,ForDynamic} core::Map<dynamic, dynamic>){(core::Map<dynamic, dynamic>) → void};
+      else
+        #t1.{core::Map::addAll}{Invariant}(x as{TypeError,ForDynamic} core::Map<dynamic, dynamic>){(core::Map<dynamic, dynamic>) → void};
+    }
+  } =>#t1;
+}
+static method main() → dynamic {
+  self::test(<core::int, core::int>{0: 0}, core::_GrowableList::_literal1<core::int>(0));
+  self::expectThrows<core::TypeError>(() → void {
+    self::test(null, core::_GrowableList::_literal1<core::int>(0));
+  });
+}
+static method expectThrows<Exception extends core::Object? = dynamic>(() → void f) → dynamic {
+  core::String? message;
+  try {
+    f(){() → void};
+    message = "Expected the function to throw an exception, but it didn't.";
+  }
+  on self::expectThrows::Exception% catch(final self::expectThrows::Exception% _) {
+  }
+  on dynamic catch(final dynamic e) {
+    message = "Expected the function to throw an exception of type '${self::expectThrows::Exception%}', but got '${e.{core::Object::runtimeType}{<object>}.{core::Type}}'.";
+  }
+  if(!(message == null)) {
+    throw message{core::String};
+  }
+}
+
+constants  {
+  #C1 = 1
+}
diff --git a/pkg/front_end/testcases/patterns/coersion_in_if_case.dart.textual_outline.expect b/pkg/front_end/testcases/patterns/coersion_in_if_case.dart.textual_outline.expect
new file mode 100644
index 0000000..970a117
--- /dev/null
+++ b/pkg/front_end/testcases/patterns/coersion_in_if_case.dart.textual_outline.expect
@@ -0,0 +1,5 @@
+test(dynamic x, dynamic list) {}
+
+main() {}
+
+expectThrows<Exception>(void Function() f) {}
diff --git a/pkg/front_end/testcases/patterns/coersion_in_if_case.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/patterns/coersion_in_if_case.dart.textual_outline_modelled.expect
new file mode 100644
index 0000000..97f6f2f
--- /dev/null
+++ b/pkg/front_end/testcases/patterns/coersion_in_if_case.dart.textual_outline_modelled.expect
@@ -0,0 +1,5 @@
+expectThrows<Exception>(void Function() f) {}
+
+main() {}
+
+test(dynamic x, dynamic list) {}
diff --git a/pkg/front_end/testcases/patterns/element_to_entry_conversion.dart.strong.expect b/pkg/front_end/testcases/patterns/element_to_entry_conversion.dart.strong.expect
index 26ef4c1..d0edd96 100644
--- a/pkg/front_end/testcases/patterns/element_to_entry_conversion.dart.strong.expect
+++ b/pkg/front_end/testcases/patterns/element_to_entry_conversion.dart.strong.expect
@@ -13,7 +13,7 @@
         hoisted core::String y;
         if(#0#0 is core::String) {
           y = #0#0{core::String};
-          #t1.{core::Map::addAll}{Invariant}(another){(core::Map<dynamic, dynamic>) → void};
+          #t1.{core::Map::addAll}{Invariant}(another as{TypeError,ForDynamic} core::Map<dynamic, dynamic>){(core::Map<dynamic, dynamic>) → void};
         }
       }
     }
@@ -30,7 +30,7 @@
         hoisted core::String y;
         if(#0#0 is core::String) {
           y = #0#0{core::String};
-          #t2.{core::Map::addAll}{Invariant}(another){(core::Map<dynamic, dynamic>) → void};
+          #t2.{core::Map::addAll}{Invariant}(another as{TypeError,ForDynamic} core::Map<dynamic, dynamic>){(core::Map<dynamic, dynamic>) → void};
         }
       }
     }
@@ -55,7 +55,7 @@
             hoisted core::int y2;
             if(#1#0 is core::int) {
               y2 = #1#0{core::int};
-              #t3.{core::Map::addAll}{Invariant}(another){(core::Map<dynamic, dynamic>) → void};
+              #t3.{core::Map::addAll}{Invariant}(another as{TypeError,ForDynamic} core::Map<dynamic, dynamic>){(core::Map<dynamic, dynamic>) → void};
             }
           }
         }
diff --git a/pkg/front_end/testcases/patterns/element_to_entry_conversion.dart.strong.modular.expect b/pkg/front_end/testcases/patterns/element_to_entry_conversion.dart.strong.modular.expect
index 26ef4c1..d0edd96 100644
--- a/pkg/front_end/testcases/patterns/element_to_entry_conversion.dart.strong.modular.expect
+++ b/pkg/front_end/testcases/patterns/element_to_entry_conversion.dart.strong.modular.expect
@@ -13,7 +13,7 @@
         hoisted core::String y;
         if(#0#0 is core::String) {
           y = #0#0{core::String};
-          #t1.{core::Map::addAll}{Invariant}(another){(core::Map<dynamic, dynamic>) → void};
+          #t1.{core::Map::addAll}{Invariant}(another as{TypeError,ForDynamic} core::Map<dynamic, dynamic>){(core::Map<dynamic, dynamic>) → void};
         }
       }
     }
@@ -30,7 +30,7 @@
         hoisted core::String y;
         if(#0#0 is core::String) {
           y = #0#0{core::String};
-          #t2.{core::Map::addAll}{Invariant}(another){(core::Map<dynamic, dynamic>) → void};
+          #t2.{core::Map::addAll}{Invariant}(another as{TypeError,ForDynamic} core::Map<dynamic, dynamic>){(core::Map<dynamic, dynamic>) → void};
         }
       }
     }
@@ -55,7 +55,7 @@
             hoisted core::int y2;
             if(#1#0 is core::int) {
               y2 = #1#0{core::int};
-              #t3.{core::Map::addAll}{Invariant}(another){(core::Map<dynamic, dynamic>) → void};
+              #t3.{core::Map::addAll}{Invariant}(another as{TypeError,ForDynamic} core::Map<dynamic, dynamic>){(core::Map<dynamic, dynamic>) → void};
             }
           }
         }
diff --git a/pkg/front_end/testcases/patterns/element_to_entry_conversion.dart.strong.transformed.expect b/pkg/front_end/testcases/patterns/element_to_entry_conversion.dart.strong.transformed.expect
index 46341de..bdab0f0 100644
--- a/pkg/front_end/testcases/patterns/element_to_entry_conversion.dart.strong.transformed.expect
+++ b/pkg/front_end/testcases/patterns/element_to_entry_conversion.dart.strong.transformed.expect
@@ -13,7 +13,7 @@
         hoisted core::String y;
         if(#0#0 is core::String) {
           y = #0#0{core::String};
-          #t1.{core::Map::addAll}{Invariant}(another){(core::Map<dynamic, dynamic>) → void};
+          #t1.{core::Map::addAll}{Invariant}(another as{TypeError,ForDynamic} core::Map<dynamic, dynamic>){(core::Map<dynamic, dynamic>) → void};
         }
       }
     }
@@ -30,7 +30,7 @@
         hoisted core::String y;
         if(#0#0 is core::String) {
           y = #0#0{core::String};
-          #t2.{core::Map::addAll}{Invariant}(another){(core::Map<dynamic, dynamic>) → void};
+          #t2.{core::Map::addAll}{Invariant}(another as{TypeError,ForDynamic} core::Map<dynamic, dynamic>){(core::Map<dynamic, dynamic>) → void};
         }
       }
     }
@@ -55,7 +55,7 @@
             hoisted core::int y2;
             if(#1#0 is core::int) {
               y2 = #1#0{core::int};
-              #t3.{core::Map::addAll}{Invariant}(another){(core::Map<dynamic, dynamic>) → void};
+              #t3.{core::Map::addAll}{Invariant}(another as{TypeError,ForDynamic} core::Map<dynamic, dynamic>){(core::Map<dynamic, dynamic>) → void};
             }
           }
         }
diff --git a/pkg/front_end/tool/coverage_merger.dart b/pkg/front_end/tool/coverage_merger.dart
index 57544ae..d4d390a 100644
--- a/pkg/front_end/tool/coverage_merger.dart
+++ b/pkg/front_end/tool/coverage_merger.dart
@@ -2,8 +2,9 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
+import 'dart:convert' show LineSplitter, utf8;
 import 'dart:io';
-import 'dart:typed_data';
+import 'dart:typed_data' show Uint8List;
 
 import 'package:_fe_analyzer_shared/src/scanner/characters.dart'
     show $SPACE, $CARET, $LF, $CR;
@@ -23,12 +24,14 @@
   Uri? packagesUri;
   bool addCommentsToFiles = false;
   bool removeCommentsFromFiles = false;
+  bool addAndRemoveCommentsInFiles = false;
 
   for (String argument in arguments) {
     const String coverage = "--coverage=";
     const String packages = "--packages=";
     const String comment = "--comment";
     const String removeComments = "--remove-comments";
+    const String addAndRemoveComments = "--add-and-remove-comments";
     if (argument.startsWith(coverage)) {
       coverageUri =
           Uri.base.resolveUri(Uri.file(argument.substring(coverage.length)));
@@ -39,6 +42,8 @@
       addCommentsToFiles = true;
     } else if (argument == removeComments) {
       removeCommentsFromFiles = true;
+    } else if (argument == addAndRemoveComments) {
+      addAndRemoveCommentsInFiles = true;
     } else {
       throw "Unsupported argument: $argument";
     }
@@ -59,11 +64,12 @@
     extraCoverageBlockIgnores: ["coverage-ignore-block(suite):"],
     addCommentsToFiles: addCommentsToFiles,
     removeCommentsFromFiles: removeCommentsFromFiles,
+    addAndRemoveCommentsInFiles: addAndRemoveCommentsInFiles,
   );
   print("Done in ${stopwatch.elapsed}");
 }
 
-Map<Uri, CoverageInfo>? mergeFromDirUri(
+Future<Map<Uri, CoverageInfo>?> mergeFromDirUri(
   Uri packagesUri,
   Uri coverageUri, {
   required bool silent,
@@ -71,7 +77,8 @@
   required List<String> extraCoverageBlockIgnores,
   bool addCommentsToFiles = false,
   bool removeCommentsFromFiles = false,
-}) {
+  bool addAndRemoveCommentsInFiles = false,
+}) async {
   void output(Object? object) {
     if (silent) return;
     print(object);
@@ -134,6 +141,7 @@
 
   Map<Uri, CoverageInfo> result = {};
 
+  List<Uri> needsFormatting = [];
   for (Uri uri in knownUris.toList()
     ..sort(((a, b) => a.toString().compareTo(b.toString())))) {
     // Don't care about coverage for testing stuff.
@@ -151,7 +159,7 @@
     List<int> hitsSorted =
         hit == null ? const [] : (hit._data.keys.toList()..sort());
 
-    CoverageInfo processInfo = process(
+    CoverageInfo processInfo = _process(
       packageConfig,
       uri,
       miss ?? const {},
@@ -160,6 +168,8 @@
       extraCoverageBlockIgnores,
       addCommentsToFiles: addCommentsToFiles,
       removeCommentsFromFiles: removeCommentsFromFiles,
+      addAndRemoveCommentsInFiles: addAndRemoveCommentsInFiles,
+      needsFormatting: needsFormatting,
     );
     if (processInfo.visualization.trim().isNotEmpty) {
       output(processInfo.visualization);
@@ -178,6 +188,26 @@
     }
   }
 
+  if (needsFormatting.isNotEmpty) {
+    Process p = await Process.start(Platform.resolvedExecutable, [
+      "format",
+      ...needsFormatting.map((uri) => new File.fromUri(uri).path)
+    ]);
+
+    p.stderr
+        .transform(utf8.decoder)
+        .transform(const LineSplitter())
+        .listen((String line) {
+      stderr.writeln("stderr> $line");
+    });
+    p.stdout
+        .transform(utf8.decoder)
+        .transform(const LineSplitter())
+        .listen((String line) {
+      stdout.writeln("stdout> $line");
+    });
+  }
+
   output("Processed $filesCount files with $errorsCount error(s) and "
       "$allCoveredCount files being covered 100%.");
   int percentHit = (hitsTotal * 100) ~/ (hitsTotal + missesTotal);
@@ -207,7 +237,7 @@
       : error = false;
 }
 
-CoverageInfo process(
+CoverageInfo _process(
   PackageConfig packageConfig,
   Uri uri,
   Set<int> untrimmedMisses,
@@ -216,6 +246,8 @@
   List<String> extraCoverageBlockIgnores, {
   bool addCommentsToFiles = false,
   bool removeCommentsFromFiles = false,
+  bool addAndRemoveCommentsInFiles = false,
+  List<Uri>? needsFormatting,
 }) {
   Uri? fileUri = packageConfig.resolve(uri);
   if (fileUri == null) {
@@ -248,40 +280,15 @@
     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;
-    }
+    List<Token> removeComments =
+        _findTokensWithCoverageIgnoreSuiteComments(token);
     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++;
-      }
+      from = _includeWhitespaceAfter(sourceText, token.charEnd);
     }
     sb.write(sourceText.substring(from));
     f.writeAsStringSync(sb.toString());
@@ -293,15 +300,36 @@
   }
 
   List<int> allSorted = [...hitsSorted, ...untrimmedMisses]..sort();
-  AstIndexerAndIgnoreCollector astIndexer =
-      AstIndexerAndIgnoreCollector.collect(
-          ast, extraCoverageIgnores, extraCoverageBlockIgnores,
-          hitsSorted: hitsSorted, allSorted: allSorted);
 
+  AstIndexerAndIgnoreCollector astIndexer;
+
+  if (addAndRemoveCommentsInFiles) {
+    // 1) Pretend like the comments that would be removed by the comment remover
+    //    aren't there (virtually removing them).
+    // 2) Now add the comments as when adding comments, but also removing the
+    //    old comments.
+    // 3) Add the new comments to the ignoredIntervals, allowing the below to
+    //    proceed as if was run on the newly commented stuff, thus returning the
+    //    (now) correct hit and miss count.
+    astIndexer = AstIndexerAndIgnoreCollector.collect(
+        ast, extraCoverageIgnores, extraCoverageBlockIgnores,
+        ignoresToIgnore: _commentTextToRemove,
+        hitsSorted: hitsSorted,
+        allSorted: allSorted);
+  } else {
+    astIndexer = AstIndexerAndIgnoreCollector.collect(
+        ast, extraCoverageIgnores, extraCoverageBlockIgnores,
+        hitsSorted: hitsSorted, allSorted: allSorted);
+  }
+
+  IntervalListBuilder? ignoredIntervalsBuilder;
+  if (addAndRemoveCommentsInFiles) {
+    ignoredIntervalsBuilder = astIndexer.ignoredStartEnd.clone();
+  }
   IntervalList ignoredIntervals =
       astIndexer.ignoredStartEnd.buildIntervalList();
 
-  if (addCommentsToFiles) {
+  if (addCommentsToFiles || addAndRemoveCommentsInFiles) {
     String sourceText = source.text;
     StringBuffer sb = new StringBuffer();
     int from = 0;
@@ -354,6 +382,53 @@
         processed.add(commentOn);
       }
     }
+
+    List<int> removeFrom = [];
+    List<int> removeTo = [];
+    if (addAndRemoveCommentsInFiles) {
+      CompilationUnitBegin unitBegin =
+          ast.children!.first as CompilationUnitBegin;
+      Token? token = unitBegin.token;
+      List<Token> removeComments =
+          _findTokensWithCoverageIgnoreSuiteComments(token);
+      for (Token token in removeComments) {
+        if (removeTo.isNotEmpty && removeTo.last == token.charOffset) {
+          // Not sure if this can happen, but if it does we extend the previous
+          // added to (i.e. end of what has to be removed).
+          removeTo[removeTo.length - 1] =
+              _includeWhitespaceAfter(sourceText, token.charEnd);
+        } else {
+          removeFrom.add(token.charOffset);
+          removeTo.add(_includeWhitespaceAfter(sourceText, token.charEnd));
+        }
+      }
+    }
+
+    void addChunk(int from, final int to) {
+      if (addAndRemoveCommentsInFiles && removeFrom.isNotEmpty) {
+        int fromIndex = binarySearch(removeFrom, from);
+        if (removeFrom[fromIndex] < from && removeTo[fromIndex] < from) {
+          fromIndex++;
+        }
+        // We have to add from `from` to `token.charOffset`, but possibly remove
+        // from `removeFrom[fromIndex]` to `removeTo[fromIndex]`,
+        // `removeFrom[fromIndex+1]` to `removeTo[fromIndex+1]` etc.
+        while (fromIndex < removeFrom.length && removeFrom[fromIndex] < to) {
+          if (from < removeFrom[fromIndex]) {
+            sb.write(sourceText.substring(from, removeFrom[fromIndex]));
+          }
+          from = removeTo[fromIndex];
+          fromIndex++;
+        }
+        // Now add any remaining text until the token.
+        if (from < to) {
+          sb.write(sourceText.substring(from, to));
+        }
+      } else {
+        sb.write(sourceText.substring(from, to));
+      }
+    }
+
     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
@@ -391,11 +466,21 @@
           token.previous?.lexeme == ":" ||
           token.previous?.lexeme == ";" ||
           token.previous?.lexeme == "=" ||
-          token.lexeme == "?.") {
+          token.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--) {
+          if (removeFrom.isNotEmpty) {
+            int fromIndex = binarySearch(removeFrom, i);
+            if (removeFrom[fromIndex] <= i && removeTo[fromIndex] >= i) {
+              // This is being removed. Change i to skip the rest.
+              i = removeFrom[fromIndex];
+              continue;
+            }
+          }
+
           int codeUnit = sourceText.codeUnitAt(i);
           if (codeUnit == $SPACE) {
             // We ignore spaces.
@@ -413,8 +498,7 @@
       if (token.precedingComments != null) {
         token = token.precedingComments!;
       }
-      String substring = sourceText.substring(from, token.charOffset);
-      sb.write(substring);
+      addChunk(from, token.charOffset);
 
       // The extra spaces at the end makes the formatter format better if for
       // instance there's comments after this.
@@ -423,14 +507,21 @@
       } else {
         sb.write("$extra// Coverage-ignore(suite): Not run.\n  ");
       }
+      ignoredIntervalsBuilder?.addIntervalIncludingEnd(
+          entry.beginToken.charOffset, entry.endToken.charEnd);
       from = token.charOffset;
     }
-    sb.write(sourceText.substring(from));
+    addChunk(from, sourceText.length);
     f.writeAsStringSync(sb.toString());
 
-    // Return a fake result.
-    return new CoverageInfo(
-        allCovered: true, missCount: -1, hitCount: -1, visualization: "fake");
+    if (addAndRemoveCommentsInFiles) {
+      ignoredIntervals = ignoredIntervalsBuilder!.buildIntervalList();
+      needsFormatting?.add(fileUri);
+    } else {
+      // 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.
@@ -569,6 +660,42 @@
       visualization: visualization.toString());
 }
 
+int _includeWhitespaceAfter(String sourceText, int from) {
+  while (sourceText.length > from &&
+      (sourceText.codeUnitAt(from) == $SPACE ||
+          sourceText.codeUnitAt(from) == $LF ||
+          sourceText.codeUnitAt(from) == $CR)) {
+    from++;
+  }
+  return from;
+}
+
+const List<String> _commentTextToRemove = const [
+  "coverage-ignore(suite): not run.",
+  "coverage-ignore-block(suite): not run.",
+];
+
+List<Token> _findTokensWithCoverageIgnoreSuiteComments(Token? token) {
+  List<Token> comments = [];
+  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 _commentTextToRemove) {
+        if (message.startsWith(coverageIgnoreString)) {
+          comments.add(comment);
+        }
+      }
+      comment = comment.next;
+    }
+    token = token.next;
+  }
+  return comments;
+}
+
 ({bool allCovered, Set<int> trimmedMisses}) _trimIgnoredAndPrintPercentages(
     StringBuffer visualization,
     IntervalList ignoredIntervals,
@@ -662,6 +789,7 @@
   final List<String> _coverageBlockIgnores = [
     "coverage-ignore-block:",
   ];
+  final List<String> _ignoresToIgnore = [];
 
   final IntervalListBuilder ignoredStartEnd = new IntervalListBuilder();
 
@@ -675,13 +803,19 @@
   late final _AstIndexerAndIgnoreCollectorBody _collectorBody =
       new _AstIndexerAndIgnoreCollectorBody(this);
 
-  static AstIndexerAndIgnoreCollector collect(ParserAstNode ast,
-      List<String> extraCoverageIgnores, List<String> extraCoverageBlockIgnores,
-      {required List<int> hitsSorted, required List<int> allSorted}) {
+  static AstIndexerAndIgnoreCollector collect(
+    ParserAstNode ast,
+    List<String> extraCoverageIgnores,
+    List<String> extraCoverageBlockIgnores, {
+    required List<int> hitsSorted,
+    required List<int> allSorted,
+    List<String> ignoresToIgnore = const [],
+  }) {
     AstIndexerAndIgnoreCollector collector =
         new AstIndexerAndIgnoreCollector._(hitsSorted, allSorted);
     collector._coverageIgnores.addAll(extraCoverageIgnores);
     collector._coverageBlockIgnores.addAll(extraCoverageBlockIgnores);
+    collector._ignoresToIgnore.addAll(ignoresToIgnore);
     ast.accept(collector);
 
     assert(collector.positionNodeIndex.length ==
@@ -706,9 +840,18 @@
       while (message.startsWith("//") || message.startsWith("/*")) {
         message = message.substring(2).trim();
       }
-      for (String coverageIgnoreString in coverageIgnores) {
+      bool skipComment = false;
+      for (String coverageIgnoreString in _ignoresToIgnore) {
         if (message.startsWith(coverageIgnoreString)) {
-          return true;
+          skipComment = true;
+          break;
+        }
+      }
+      if (!skipComment) {
+        for (String coverageIgnoreString in coverageIgnores) {
+          if (message.startsWith(coverageIgnoreString)) {
+            return true;
+          }
         }
       }
       comment = comment.next;
@@ -735,7 +878,9 @@
   /// 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,
+      final Token tokenWithPossibleComment,
+      final Token beginToken,
+      final Token endToken,
       {required bool allowReplace,
       bool isBlock = false,
       bool allowOnBraceStart = false}) {
@@ -1176,6 +1321,19 @@
   }
 
   @override
+  void visitIndexedExpressionHandle(IndexedExpressionHandle node) {
+    // Given `a?[b]` if `a` is null `b` won't execute.
+    // Having the comment before the `?` looks better.
+    if (node.question != null &&
+        _collector._checkCommentAndIgnoreCoverageWithBeginAndEnd(
+            node.question!, node.openSquareBracket, node.closeSquareBracket,
+            allowReplace: true)) {
+      return;
+    }
+    super.visitIndexedExpressionHandle(node);
+  }
+
+  @override
   void visitThenControlFlowHandle(ThenControlFlowHandle node) {
     ParserAstNode? parent = node.parent;
     if (parent is IfControlFlowEnd) {
diff --git a/pkg/front_end/tool/dart_doctest_impl.dart b/pkg/front_end/tool/dart_doctest_impl.dart
index a0de469..cf32984 100644
--- a/pkg/front_end/tool/dart_doctest_impl.dart
+++ b/pkg/front_end/tool/dart_doctest_impl.dart
@@ -237,7 +237,9 @@
             message.toString().substring("$_portMessageBad: ".length);
         result.add(new TestResult(currentTest!, TestOutcome.Failed)
           ..message = strippedMessage);
-        _print(strippedMessage);
+        _print("Failure:\n"
+            "Test from ${currentTest!.location} failed with this message:\n"
+            "$strippedMessage\n");
       } else if (message.toString().startsWith("$_portMessageCrash: ")) {
         String strippedMessage =
             message.toString().substring("$_portMessageCrash: ".length);
@@ -457,39 +459,47 @@
 const int $SPACE = 32;
 const int $STAR = 42;
 
-class Test {}
+sealed class Test {
+  String get location;
+}
 
 class ExpectTest implements Test {
   final String call;
   final String result;
+  @override
+  final String location;
 
-  ExpectTest(this.call, this.result);
+  ExpectTest(this.call, this.result, this.location);
 
   @override
   bool operator ==(Object other) {
     if (other is! ExpectTest) return false;
     if (other.call != call) return false;
     if (other.result != result) return false;
+    if (other.location != location) return false;
     return true;
   }
 
   @override
   String toString() {
-    return "ExpectTest[$call, $result]";
+    return "ExpectTest[$call, $result, $location]";
   }
 }
 
 class TestParseError implements Test {
   final String message;
   final int position;
+  @override
+  final String location;
 
-  TestParseError(this.message, this.position);
+  TestParseError(this.message, this.position, this.location);
 
   @override
   bool operator ==(Object other) {
     if (other is! TestParseError) return false;
     if (other.message != message) return false;
     if (other.position != position) return false;
+    if (other.location != location) return false;
     return true;
   }
 
@@ -544,6 +554,12 @@
     return result;
   }
 
+  String getLocation(int offset) {
+    return source
+        .getLocation(source.importUri ?? source.fileUri!, offset)
+        .toString();
+  }
+
   Test scanDartDoc(int scanOffset) {
     final Token firstToken =
         scanRawBytes(utf8.encode(comments.substring(scanOffset)));
@@ -569,13 +585,18 @@
       StringBuffer sb = new StringBuffer();
       int firstPosition = _createParseErrorMessages(
           listener, sb, commentsData, scanOffset, source);
-      return new TestParseError(sb.toString(), firstPosition);
+      return new TestParseError(
+        sb.toString(),
+        firstPosition,
+        getLocation(firstPosition),
+      );
     } else if (!identical(",", comma.stringValue)) {
       int position = commentsData.charOffset + scanOffset + comma.charOffset;
       Message message = codes.templateExpectedButGot.withArguments(',');
       return new TestParseError(
         _createParseErrorMessage(source, position, comma, comma, message),
         position,
+        getLocation(position),
       );
     }
 
@@ -586,13 +607,18 @@
       StringBuffer sb = new StringBuffer();
       int firstPosition = _createParseErrorMessages(
           listener, sb, commentsData, scanOffset, source);
-      return new TestParseError(sb.toString(), firstPosition);
+      return new TestParseError(
+        sb.toString(),
+        firstPosition,
+        getLocation(firstPosition),
+      );
     } else if (!identical(")", endParen.stringValue)) {
       int position = commentsData.charOffset + scanOffset + endParen.charOffset;
       Message message = codes.templateExpectedButGot.withArguments(')');
       return new TestParseError(
         _createParseErrorMessage(source, position, comma, comma, message),
         position,
+        getLocation(position),
       );
     }
 
@@ -606,6 +632,7 @@
     return new ExpectTest(
       comments.substring(startPos, midEndPos),
       comments.substring(midStartPos, endPos),
+      getLocation(commentsData.charOffset + startPos),
     );
   }
 
diff --git a/pkg/front_end/tool/interval_list.dart b/pkg/front_end/tool/interval_list.dart
index 3ff3f1d..65c659a 100644
--- a/pkg/front_end/tool/interval_list.dart
+++ b/pkg/front_end/tool/interval_list.dart
@@ -7,8 +7,16 @@
 // Based on (as in mostly a copy from) the _IntervalListBuilder in
 // package:kernel/class_hierarchy.dart.
 class IntervalListBuilder {
+  bool _finished = false;
   final List<int> _events = <int>[];
 
+  IntervalListBuilder clone() {
+    if (_finished) throw "Can't clone an already finished builder.";
+    IntervalListBuilder clone = new IntervalListBuilder();
+    clone._events.addAll(_events);
+    return clone;
+  }
+
   void addIntervalIncludingEnd(int start, int end) {
     // Add an event point for each interval end point, using the low bit to
     // distinguish opening from closing end points. Closing end points should
@@ -31,6 +39,7 @@
   }
 
   IntervalList buildIntervalList() {
+    if (_finished) throw "Can't build an already finished builder.";
     // Sort the event points and sweep left to right while tracking how many
     // intervals we are currently inside.  Record an interval end point when the
     // number of intervals drop to zero or increase from zero to one.
@@ -61,6 +70,8 @@
     for (int i = 0; i < storeIndex; ++i) {
       result[i] = _events[i];
     }
+
+    _finished = true;
     return new IntervalList._(result);
   }
 }
diff --git a/tools/VERSION b/tools/VERSION
index f4f3383..5b019ed 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 3
 MINOR 6
 PATCH 0
-PRERELEASE 108
+PRERELEASE 109
 PRERELEASE_PATCH 0