[kernel] Initial migration of package kernel wave 1

This CL completes the migration of the first wave of
interdependent libraries in package:kernel, including ast.dart.

In order to ensure non-nullability on AST properties, the Transformer
has been split in 2 variants: Transformer which doesn't support
removal of nodes and RemovingTransformer which supports removal where
allowed by the context using 'removal sentinels'.

Start reviewing Transformer and RemovingTransformer in visitors.dart
since many of the changes are caused by the changes here.

Included in the migration are the mixin_deduplication.dart and
unreachable_code_elimination.dart since these needed porting to
the RemovingTransformer which was aided by opting in the libraries
which only depended on ast.dart.

TEST=existing

Change-Id: I9e63b985bd24896c25edd4ee51e37770187bcc17
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/184786
Commit-Queue: Johnni Winther <johnniwinther@google.com>
Reviewed-by: Jens Johansen <jensj@google.com>
diff --git a/pkg/compiler/lib/src/ir/constants.dart b/pkg/compiler/lib/src/ir/constants.dart
index d1ddfd7..861711b 100644
--- a/pkg/compiler/lib/src/ir/constants.dart
+++ b/pkg/compiler/lib/src/ir/constants.dart
@@ -204,11 +204,21 @@
   }
 
   @override
+  R accept1<R, A>(ir.TreeVisitor1<R, A> v, A arg) {
+    throw new UnsupportedError("ConstantReference.accept");
+  }
+
+  @override
   transformChildren(ir.Transformer v) {
     throw new UnsupportedError("ConstantReference.transformChildren");
   }
 
   @override
+  transformOrRemoveChildren(ir.RemovingTransformer v) {
+    throw new UnsupportedError("ConstantReference.transformOrRemoveChildren");
+  }
+
+  @override
   int get hashCode => 13 * constant.hashCode;
 
   @override
diff --git a/pkg/front_end/lib/src/fasta/kernel/collections.dart b/pkg/front_end/lib/src/fasta/kernel/collections.dart
index 74a18c1..5a055f9 100644
--- a/pkg/front_end/lib/src/fasta/kernel/collections.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/collections.dart
@@ -6,19 +6,7 @@
 
 library fasta.collections;
 
-import 'package:kernel/ast.dart'
-    show
-        DartType,
-        Expression,
-        ExpressionStatement,
-        MapEntry,
-        NullLiteral,
-        Statement,
-        TreeNode,
-        VariableDeclaration,
-        setParents,
-        transformList,
-        visitList;
+import 'package:kernel/ast.dart';
 
 import 'package:kernel/src/printer.dart';
 
@@ -90,14 +78,22 @@
   }
 
   @override
-  visitChildren(Visitor<Object> v) {
+  void visitChildren(Visitor<Object> v) {
     expression?.accept(v);
   }
 
   @override
-  transformChildren(Transformer v) {
+  void transformChildren(Transformer v) {
     if (expression != null) {
-      expression = expression.accept<TreeNode>(v);
+      expression = v.transform(expression);
+      expression?.parent = this;
+    }
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    if (expression != null) {
+      expression = v.transformOrRemoveExpression(expression);
       expression?.parent = this;
     }
   }
@@ -136,24 +132,40 @@
   }
 
   @override
-  visitChildren(Visitor<Object> v) {
+  void visitChildren(Visitor<Object> v) {
     condition?.accept(v);
     then?.accept(v);
     otherwise?.accept(v);
   }
 
   @override
-  transformChildren(Transformer v) {
+  void transformChildren(Transformer v) {
     if (condition != null) {
-      condition = condition.accept<TreeNode>(v);
+      condition = v.transform(condition);
       condition?.parent = this;
     }
     if (then != null) {
-      then = then.accept<TreeNode>(v);
+      then = v.transform(then);
       then?.parent = this;
     }
     if (otherwise != null) {
-      otherwise = otherwise.accept<TreeNode>(v);
+      otherwise = v.transform(otherwise);
+      otherwise?.parent = this;
+    }
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    if (condition != null) {
+      condition = v.transformOrRemoveExpression(condition);
+      condition?.parent = this;
+    }
+    if (then != null) {
+      then = v.transformOrRemoveExpression(then);
+      then?.parent = this;
+    }
+    if (otherwise != null) {
+      otherwise = v.transformOrRemoveExpression(otherwise);
       otherwise?.parent = this;
     }
   }
@@ -211,7 +223,7 @@
   }
 
   @override
-  visitChildren(Visitor<Object> v) {
+  void visitChildren(Visitor<Object> v) {
     visitList(variables, v);
     condition?.accept(v);
     visitList(updates, v);
@@ -219,15 +231,29 @@
   }
 
   @override
-  transformChildren(Transformer v) {
-    transformList(variables, v, this);
+  void transformChildren(Transformer v) {
+    v.transformList(variables, this);
     if (condition != null) {
-      condition = condition.accept<TreeNode>(v);
+      condition = v.transform(condition);
       condition?.parent = this;
     }
-    transformList(updates, v, this);
+    v.transformList(updates, this);
     if (body != null) {
-      body = body.accept<TreeNode>(v);
+      body = v.transform(body);
+      body?.parent = this;
+    }
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    v.transformVariableDeclarationList(variables, this);
+    if (condition != null) {
+      condition = v.transformOrRemoveExpression(condition);
+      condition?.parent = this;
+    }
+    v.transformExpressionList(updates, this);
+    if (body != null) {
+      body = v.transformOrRemoveExpression(body);
       body?.parent = this;
     }
   }
@@ -284,7 +310,7 @@
         ..fileOffset = syntheticAssignment.fileOffset)
       : expressionEffects;
 
-  visitChildren(Visitor<Object> v) {
+  void visitChildren(Visitor<Object> v) {
     variable?.accept(v);
     iterable?.accept(v);
     syntheticAssignment?.accept(v);
@@ -293,29 +319,57 @@
     problem?.accept(v);
   }
 
-  transformChildren(Transformer v) {
+  void transformChildren(Transformer v) {
     if (variable != null) {
-      variable = variable.accept<TreeNode>(v);
+      variable = v.transform(variable);
       variable?.parent = this;
     }
     if (iterable != null) {
-      iterable = iterable.accept<TreeNode>(v);
+      iterable = v.transform(iterable);
       iterable?.parent = this;
     }
     if (syntheticAssignment != null) {
-      syntheticAssignment = syntheticAssignment.accept<TreeNode>(v);
+      syntheticAssignment = v.transform(syntheticAssignment);
       syntheticAssignment?.parent = this;
     }
     if (expressionEffects != null) {
-      expressionEffects = expressionEffects.accept<TreeNode>(v);
+      expressionEffects = v.transform(expressionEffects);
       expressionEffects?.parent = this;
     }
     if (body != null) {
-      body = body.accept<TreeNode>(v);
+      body = v.transform(body);
       body?.parent = this;
     }
     if (problem != null) {
-      problem = problem.accept<TreeNode>(v);
+      problem = v.transform(problem);
+      problem?.parent = this;
+    }
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    if (variable != null) {
+      variable = v.transformOrRemoveVariableDeclaration(variable);
+      variable?.parent = this;
+    }
+    if (iterable != null) {
+      iterable = v.transformOrRemoveExpression(iterable);
+      iterable?.parent = this;
+    }
+    if (syntheticAssignment != null) {
+      syntheticAssignment = v.transformOrRemoveExpression(syntheticAssignment);
+      syntheticAssignment?.parent = this;
+    }
+    if (expressionEffects != null) {
+      expressionEffects = v.transformOrRemoveStatement(expressionEffects);
+      expressionEffects?.parent = this;
+    }
+    if (body != null) {
+      body = v.transformOrRemoveExpression(body);
+      body?.parent = this;
+    }
+    if (problem != null) {
+      problem = v.transformOrRemoveExpression(problem);
       problem?.parent = this;
     }
   }
@@ -372,6 +426,9 @@
   R accept<R>(TreeVisitor<R> v) => v.defaultTreeNode(this);
 
   @override
+  R accept1<R, A>(TreeVisitor1<R, A> v, A arg) => v.defaultTreeNode(this, arg);
+
+  @override
   String toStringInternal() => toText(defaultAstTextStrategy);
 
   @override
@@ -398,14 +455,22 @@
   }
 
   @override
-  visitChildren(Visitor<Object> v) {
+  void visitChildren(Visitor<Object> v) {
     expression?.accept(v);
   }
 
   @override
-  transformChildren(Transformer v) {
+  void transformChildren(Transformer v) {
     if (expression != null) {
-      expression = expression.accept<TreeNode>(v);
+      expression = v.transform(expression);
+      expression?.parent = this;
+    }
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    if (expression != null) {
+      expression = v.transformOrRemoveExpression(expression);
       expression?.parent = this;
     }
   }
@@ -434,24 +499,40 @@
   }
 
   @override
-  visitChildren(Visitor<Object> v) {
+  void visitChildren(Visitor<Object> v) {
     condition?.accept(v);
     then?.accept(v);
     otherwise?.accept(v);
   }
 
   @override
-  transformChildren(Transformer v) {
+  void transformChildren(Transformer v) {
     if (condition != null) {
-      condition = condition.accept<TreeNode>(v);
+      condition = v.transform(condition);
       condition?.parent = this;
     }
     if (then != null) {
-      then = then.accept<TreeNode>(v);
+      then = v.transform(then);
       then?.parent = this;
     }
     if (otherwise != null) {
-      otherwise = otherwise.accept<TreeNode>(v);
+      otherwise = v.transform(otherwise);
+      otherwise?.parent = this;
+    }
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    if (condition != null) {
+      condition = v.transformOrRemoveExpression(condition);
+      condition?.parent = this;
+    }
+    if (then != null) {
+      then = v.transformOrRemove(then, dummyMapEntry);
+      then?.parent = this;
+    }
+    if (otherwise != null) {
+      otherwise = v.transformOrRemove(otherwise, dummyMapEntry);
       otherwise?.parent = this;
     }
   }
@@ -482,7 +563,7 @@
   }
 
   @override
-  visitChildren(Visitor<Object> v) {
+  void visitChildren(Visitor<Object> v) {
     visitList(variables, v);
     condition?.accept(v);
     visitList(updates, v);
@@ -490,15 +571,29 @@
   }
 
   @override
-  transformChildren(Transformer v) {
-    transformList(variables, v, this);
+  void transformChildren(Transformer v) {
+    v.transformList(variables, this);
     if (condition != null) {
-      condition = condition.accept<TreeNode>(v);
+      condition = v.transform(condition);
       condition?.parent = this;
     }
-    transformList(updates, v, this);
+    v.transformList(updates, this);
     if (body != null) {
-      body = body.accept<TreeNode>(v);
+      body = v.transform(body);
+      body?.parent = this;
+    }
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    v.transformVariableDeclarationList(variables, this);
+    if (condition != null) {
+      condition = v.transformOrRemoveExpression(condition);
+      condition?.parent = this;
+    }
+    v.transformExpressionList(updates, this);
+    if (body != null) {
+      body = v.transformOrRemove(body, dummyMapEntry);
       body?.parent = this;
     }
   }
@@ -541,7 +636,7 @@
         ..fileOffset = syntheticAssignment.fileOffset)
       : expressionEffects;
 
-  visitChildren(Visitor<Object> v) {
+  void visitChildren(Visitor<Object> v) {
     variable?.accept(v);
     iterable?.accept(v);
     syntheticAssignment?.accept(v);
@@ -550,29 +645,57 @@
     problem?.accept(v);
   }
 
-  transformChildren(Transformer v) {
+  void transformChildren(Transformer v) {
     if (variable != null) {
-      variable = variable.accept<TreeNode>(v);
+      variable = v.transform(variable);
       variable?.parent = this;
     }
     if (iterable != null) {
-      iterable = iterable.accept<TreeNode>(v);
+      iterable = v.transform(iterable);
       iterable?.parent = this;
     }
     if (syntheticAssignment != null) {
-      syntheticAssignment = syntheticAssignment.accept<TreeNode>(v);
+      syntheticAssignment = v.transform(syntheticAssignment);
       syntheticAssignment?.parent = this;
     }
     if (expressionEffects != null) {
-      expressionEffects = expressionEffects.accept<TreeNode>(v);
+      expressionEffects = v.transform(expressionEffects);
       expressionEffects?.parent = this;
     }
     if (body != null) {
-      body = body.accept<TreeNode>(v);
+      body = v.transform(body);
       body?.parent = this;
     }
     if (problem != null) {
-      problem = problem.accept<TreeNode>(v);
+      problem = v.transform(problem);
+      problem?.parent = this;
+    }
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    if (variable != null) {
+      variable = v.transformOrRemoveVariableDeclaration(variable);
+      variable?.parent = this;
+    }
+    if (iterable != null) {
+      iterable = v.transformOrRemoveExpression(iterable);
+      iterable?.parent = this;
+    }
+    if (syntheticAssignment != null) {
+      syntheticAssignment = v.transformOrRemoveExpression(syntheticAssignment);
+      syntheticAssignment?.parent = this;
+    }
+    if (expressionEffects != null) {
+      expressionEffects = v.transformOrRemoveStatement(expressionEffects);
+      expressionEffects?.parent = this;
+    }
+    if (body != null) {
+      body = v.transformOrRemove(body, dummyMapEntry);
+      body?.parent = this;
+    }
+    if (problem != null) {
+      problem = v.transformOrRemoveExpression(problem);
       problem?.parent = this;
     }
   }
diff --git a/pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart b/pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart
index e156e83..9709c98 100644
--- a/pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart
@@ -155,7 +155,7 @@
       typeEnvironment,
       errorReporter,
       evaluationMode);
-  constantsTransformer.visitProcedure(procedure);
+  constantsTransformer.visitProcedure(procedure, null);
 }
 
 enum EvaluationMode {
@@ -327,7 +327,7 @@
   Constant visitUnevaluatedConstant(UnevaluatedConstant node) => null;
 }
 
-class ConstantsTransformer extends Transformer {
+class ConstantsTransformer extends RemovingTransformer {
   final ConstantsBackend backend;
   final ConstantEvaluator constantEvaluator;
   final TypeEnvironment typeEnvironment;
@@ -367,12 +367,12 @@
 
     transformAnnotations(library.annotations, library);
 
-    transformList(library.dependencies, this, library);
-    transformList(library.parts, this, library);
-    transformList(library.typedefs, this, library);
-    transformList(library.classes, this, library);
-    transformList(library.procedures, this, library);
-    transformList(library.fields, this, library);
+    transformLibraryDependencyList(library.dependencies, library);
+    transformLibraryPartList(library.parts, library);
+    transformTypedefList(library.typedefs, library);
+    transformClassList(library.classes, library);
+    transformProcedureList(library.procedures, library);
+    transformFieldList(library.fields, library);
 
     if (!keepFields) {
       // The transformer API does not iterate over `Library.additionalExports`,
@@ -385,7 +385,7 @@
   }
 
   @override
-  LibraryPart visitLibraryPart(LibraryPart node) {
+  LibraryPart visitLibraryPart(LibraryPart node, TreeNode removalSentinel) {
     constantEvaluator.withNewEnvironment(() {
       transformAnnotations(node.annotations, node);
     });
@@ -393,7 +393,8 @@
   }
 
   @override
-  LibraryDependency visitLibraryDependency(LibraryDependency node) {
+  LibraryDependency visitLibraryDependency(
+      LibraryDependency node, TreeNode removalSentinel) {
     constantEvaluator.withNewEnvironment(() {
       transformAnnotations(node.annotations, node);
     });
@@ -401,78 +402,80 @@
   }
 
   @override
-  Class visitClass(Class node) {
+  Class visitClass(Class node, TreeNode removalSentinel) {
     StaticTypeContext oldStaticTypeContext = _staticTypeContext;
     _staticTypeContext = new StaticTypeContext.forAnnotations(
         node.enclosingLibrary, typeEnvironment);
     constantEvaluator.withNewEnvironment(() {
       transformAnnotations(node.annotations, node);
-      transformList(node.fields, this, node);
-      transformList(node.typeParameters, this, node);
-      transformList(node.constructors, this, node);
-      transformList(node.procedures, this, node);
-      transformList(node.redirectingFactoryConstructors, this, node);
+      transformFieldList(node.fields, node);
+      transformTypeParameterList(node.typeParameters, node);
+      transformConstructorList(node.constructors, node);
+      transformProcedureList(node.procedures, node);
+      transformRedirectingFactoryConstructorList(
+          node.redirectingFactoryConstructors, node);
     });
     _staticTypeContext = oldStaticTypeContext;
     return node;
   }
 
   @override
-  Procedure visitProcedure(Procedure node) {
+  Procedure visitProcedure(Procedure node, TreeNode removalSentinel) {
     StaticTypeContext oldStaticTypeContext = _staticTypeContext;
     _staticTypeContext = new StaticTypeContext(node, typeEnvironment);
     constantEvaluator.withNewEnvironment(() {
       transformAnnotations(node.annotations, node);
-      node.function = node.function.accept<TreeNode>(this)..parent = node;
+      node.function = transform(node.function)..parent = node;
     });
     _staticTypeContext = oldStaticTypeContext;
     return node;
   }
 
   @override
-  Constructor visitConstructor(Constructor node) {
+  Constructor visitConstructor(Constructor node, TreeNode removalSentinel) {
     StaticTypeContext oldStaticTypeContext = _staticTypeContext;
     _staticTypeContext = new StaticTypeContext(node, typeEnvironment);
     constantEvaluator.withNewEnvironment(() {
       transformAnnotations(node.annotations, node);
-      transformList(node.initializers, this, node);
-      node.function = node.function.accept<TreeNode>(this)..parent = node;
+      transformInitializerList(node.initializers, node);
+      node.function = transform(node.function)..parent = node;
     });
     _staticTypeContext = oldStaticTypeContext;
     return node;
   }
 
   @override
-  Typedef visitTypedef(Typedef node) {
+  Typedef visitTypedef(Typedef node, TreeNode removalSentinel) {
     constantEvaluator.withNewEnvironment(() {
       transformAnnotations(node.annotations, node);
-      transformList(node.typeParameters, this, node);
-      transformList(node.typeParametersOfFunctionType, this, node);
-      transformList(node.positionalParameters, this, node);
-      transformList(node.namedParameters, this, node);
+      transformTypeParameterList(node.typeParameters, node);
+      transformTypeParameterList(node.typeParametersOfFunctionType, node);
+      transformVariableDeclarationList(node.positionalParameters, node);
+      transformVariableDeclarationList(node.namedParameters, node);
     });
     return node;
   }
 
   @override
   RedirectingFactoryConstructor visitRedirectingFactoryConstructor(
-      RedirectingFactoryConstructor node) {
+      RedirectingFactoryConstructor node, TreeNode removalSentinel) {
     // Currently unreachable as the compiler doesn't produce
     // RedirectingFactoryConstructor.
     StaticTypeContext oldStaticTypeContext = _staticTypeContext;
     _staticTypeContext = new StaticTypeContext(node, typeEnvironment);
     constantEvaluator.withNewEnvironment(() {
       transformAnnotations(node.annotations, node);
-      transformList(node.typeParameters, this, node);
-      transformList(node.positionalParameters, this, node);
-      transformList(node.namedParameters, this, node);
+      transformTypeParameterList(node.typeParameters, node);
+      transformVariableDeclarationList(node.positionalParameters, node);
+      transformVariableDeclarationList(node.namedParameters, node);
     });
     _staticTypeContext = oldStaticTypeContext;
     return node;
   }
 
   @override
-  TypeParameter visitTypeParameter(TypeParameter node) {
+  TypeParameter visitTypeParameter(
+      TypeParameter node, TreeNode removalSentinel) {
     transformAnnotations(node.annotations, node);
     return node;
   }
@@ -495,8 +498,8 @@
   // Handle definition of constants:
 
   @override
-  FunctionNode visitFunctionNode(FunctionNode node) {
-    transformList(node.typeParameters, this, node);
+  FunctionNode visitFunctionNode(FunctionNode node, TreeNode removalSentinel) {
+    transformTypeParameterList(node.typeParameters, node);
     final int positionalParameterCount = node.positionalParameters.length;
     for (int i = 0; i < positionalParameterCount; ++i) {
       final VariableDeclaration variable = node.positionalParameters[i];
@@ -516,13 +519,14 @@
       }
     }
     if (node.body != null) {
-      node.body = node.body.accept<TreeNode>(this)..parent = node;
+      node.body = transform(node.body)..parent = node;
     }
     return node;
   }
 
   @override
-  VariableDeclaration visitVariableDeclaration(VariableDeclaration node) {
+  Statement visitVariableDeclaration(
+      VariableDeclaration node, TreeNode removalSentinel) {
     transformAnnotations(node.annotations, node);
 
     if (node.initializer != null) {
@@ -538,19 +542,18 @@
             // If the constant is unevaluated we need to keep the expression,
             // so that, in the case the constant contains error but the local
             // is unused, the error will still be reported.
-            return null;
+            return removalSentinel /*!*/ ?? node;
           }
         }
       } else {
-        node.initializer = node.initializer.accept<TreeNode>(this)
-          ..parent = node;
+        node.initializer = transform(node.initializer)..parent = node;
       }
     }
     return node;
   }
 
   @override
-  Field visitField(Field node) {
+  Field visitField(Field node, TreeNode removalSentinel) {
     StaticTypeContext oldStaticTypeContext = _staticTypeContext;
     _staticTypeContext = new StaticTypeContext(node, typeEnvironment);
     Field field = constantEvaluator.withNewEnvironment(() {
@@ -562,13 +565,12 @@
 
         // If this constant is inlined, remove it.
         if (!keepFields && shouldInline(node.initializer)) {
-          return null;
+          return removalSentinel;
         }
       } else {
         transformAnnotations(node.annotations, node);
         if (node.initializer != null) {
-          node.initializer = node.initializer.accept<TreeNode>(this)
-            ..parent = node;
+          node.initializer = transform(node.initializer)..parent = node;
         }
       }
       return node;
@@ -580,7 +582,7 @@
   // Handle use-sites of constants (and "inline" constant expressions):
 
   @override
-  Expression visitSymbolLiteral(SymbolLiteral node) {
+  Expression visitSymbolLiteral(SymbolLiteral node, TreeNode removalSentinel) {
     return makeConstantExpression(
         constantEvaluator.evaluate(_staticTypeContext, node), node);
   }
@@ -591,9 +593,9 @@
   }
 
   @override
-  Expression visitEqualsCall(EqualsCall node) {
-    Expression left = node.left.accept<TreeNode>(this);
-    Expression right = node.right.accept<TreeNode>(this);
+  Expression visitEqualsCall(EqualsCall node, TreeNode removalSentinel) {
+    Expression left = transform(node.left);
+    Expression right = transform(node.right);
     if (_isNull(left)) {
       return new EqualsNull(right, isNot: node.isNot)
         ..fileOffset = node.fileOffset;
@@ -607,7 +609,7 @@
   }
 
   @override
-  Expression visitStaticGet(StaticGet node) {
+  Expression visitStaticGet(StaticGet node, TreeNode removalSentinel) {
     final Member target = node.target;
     if (target is Field && target.isConst) {
       // Make sure the initializer is evaluated first.
@@ -623,27 +625,28 @@
     } else if (target is Procedure && target.kind == ProcedureKind.Method) {
       return evaluateAndTransformWithContext(node, node);
     }
-    return super.visitStaticGet(node);
+    return super.visitStaticGet(node, removalSentinel);
   }
 
   @override
-  Expression visitStaticTearOff(StaticTearOff node) {
+  Expression visitStaticTearOff(StaticTearOff node, TreeNode removalSentinel) {
     final Member target = node.target;
     if (target is Procedure && target.kind == ProcedureKind.Method) {
       return evaluateAndTransformWithContext(node, node);
     }
-    return super.visitStaticTearOff(node);
+    return super.visitStaticTearOff(node, removalSentinel);
   }
 
   @override
-  SwitchCase visitSwitchCase(SwitchCase node) {
+  SwitchCase visitSwitchCase(SwitchCase node, TreeNode removalSentinel) {
     transformExpressions(node.expressions, node);
-    return super.visitSwitchCase(node);
+    return super.visitSwitchCase(node, removalSentinel);
   }
 
   @override
-  SwitchStatement visitSwitchStatement(SwitchStatement node) {
-    SwitchStatement result = super.visitSwitchStatement(node);
+  SwitchStatement visitSwitchStatement(
+      SwitchStatement node, TreeNode removalSentinel) {
+    SwitchStatement result = super.visitSwitchStatement(node, removalSentinel);
     Library library = constantEvaluator.libraryOf(node);
     if (library != null && library.isNonNullableByDefault) {
       for (SwitchCase switchCase in node.cases) {
@@ -670,7 +673,7 @@
   }
 
   @override
-  Expression visitVariableGet(VariableGet node) {
+  Expression visitVariableGet(VariableGet node, TreeNode removalSentinel) {
     final VariableDeclaration variable = node.variable;
     if (variable.isConst) {
       variable.initializer =
@@ -680,74 +683,80 @@
         return evaluateAndTransformWithContext(node, node);
       }
     }
-    return super.visitVariableGet(node);
+    return super.visitVariableGet(node, removalSentinel);
   }
 
   @override
-  Expression visitListLiteral(ListLiteral node) {
+  Expression visitListLiteral(ListLiteral node, TreeNode removalSentinel) {
     if (node.isConst) {
       return evaluateAndTransformWithContext(node, node);
     }
-    return super.visitListLiteral(node);
+    return super.visitListLiteral(node, removalSentinel);
   }
 
   @override
-  Expression visitListConcatenation(ListConcatenation node) {
+  Expression visitListConcatenation(
+      ListConcatenation node, TreeNode removalSentinel) {
     return evaluateAndTransformWithContext(node, node);
   }
 
   @override
-  Expression visitSetLiteral(SetLiteral node) {
+  Expression visitSetLiteral(SetLiteral node, TreeNode removalSentinel) {
     if (node.isConst) {
       return evaluateAndTransformWithContext(node, node);
     }
-    return super.visitSetLiteral(node);
+    return super.visitSetLiteral(node, removalSentinel);
   }
 
   @override
-  Expression visitSetConcatenation(SetConcatenation node) {
+  Expression visitSetConcatenation(
+      SetConcatenation node, TreeNode removalSentinel) {
     return evaluateAndTransformWithContext(node, node);
   }
 
   @override
-  Expression visitMapLiteral(MapLiteral node) {
+  Expression visitMapLiteral(MapLiteral node, TreeNode removalSentinel) {
     if (node.isConst) {
       return evaluateAndTransformWithContext(node, node);
     }
-    return super.visitMapLiteral(node);
+    return super.visitMapLiteral(node, removalSentinel);
   }
 
   @override
-  Expression visitTypeLiteral(TypeLiteral node) {
+  Expression visitTypeLiteral(TypeLiteral node, TreeNode removalSentinel) {
     if (!containsFreeTypeVariables(node.type)) {
       return evaluateAndTransformWithContext(node, node);
     }
-    return super.visitTypeLiteral(node);
+    return super.visitTypeLiteral(node, removalSentinel);
   }
 
   @override
-  Expression visitMapConcatenation(MapConcatenation node) {
+  Expression visitMapConcatenation(
+      MapConcatenation node, TreeNode removalSentinel) {
     return evaluateAndTransformWithContext(node, node);
   }
 
   @override
-  Expression visitConstructorInvocation(ConstructorInvocation node) {
+  Expression visitConstructorInvocation(
+      ConstructorInvocation node, TreeNode removalSentinel) {
     if (node.isConst) {
       return evaluateAndTransformWithContext(node, node);
     }
-    return super.visitConstructorInvocation(node);
+    return super.visitConstructorInvocation(node, removalSentinel);
   }
 
   @override
-  Expression visitStaticInvocation(StaticInvocation node) {
+  Expression visitStaticInvocation(
+      StaticInvocation node, TreeNode removalSentinel) {
     if (node.isConst) {
       return evaluateAndTransformWithContext(node, node);
     }
-    return super.visitStaticInvocation(node);
+    return super.visitStaticInvocation(node, removalSentinel);
   }
 
   @override
-  Expression visitConstantExpression(ConstantExpression node) {
+  Expression visitConstantExpression(
+      ConstantExpression node, TreeNode removalSentinel) {
     Constant constant = node.constant;
     if (constant is UnevaluatedConstant) {
       Expression expression = constant.expression;
diff --git a/pkg/front_end/lib/src/fasta/kernel/forest.dart b/pkg/front_end/lib/src/fasta/kernel/forest.dart
index 18ac475..9be6294 100644
--- a/pkg/front_end/lib/src/fasta/kernel/forest.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/forest.dart
@@ -775,6 +775,10 @@
     throw unsupported("transformChildren", fileOffset, uri);
   }
 
+  transformOrRemoveChildren(v) {
+    throw unsupported("transformOrRemoveChildren", fileOffset, uri);
+  }
+
   @override
   String toString() {
     return "_VariablesDeclaration(${toStringInternal()})";
diff --git a/pkg/front_end/lib/src/fasta/kernel/internal_ast.dart b/pkg/front_end/lib/src/fasta/kernel/internal_ast.dart
index 18e41e7..faf4716 100644
--- a/pkg/front_end/lib/src/fasta/kernel/internal_ast.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/internal_ast.dart
@@ -260,23 +260,47 @@
   @override
   void transformChildren(Transformer v) {
     if (variable != null) {
-      variable = variable.accept<TreeNode>(v);
+      variable = v.transform(variable);
       variable?.parent = this;
     }
     if (iterable != null) {
-      iterable = iterable.accept<TreeNode>(v);
+      iterable = v.transform(iterable);
       iterable?.parent = this;
     }
     if (syntheticAssignment != null) {
-      syntheticAssignment = syntheticAssignment.accept<TreeNode>(v);
+      syntheticAssignment = v.transform(syntheticAssignment);
       syntheticAssignment?.parent = this;
     }
     if (expressionEffects != null) {
-      expressionEffects = expressionEffects.accept<TreeNode>(v);
+      expressionEffects = v.transform(expressionEffects);
       expressionEffects?.parent = this;
     }
     if (body != null) {
-      body = body.accept<TreeNode>(v);
+      body = v.transform(body);
+      body?.parent = this;
+    }
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    if (variable != null) {
+      variable = v.transform(variable);
+      variable?.parent = this;
+    }
+    if (iterable != null) {
+      iterable = v.transform(iterable);
+      iterable?.parent = this;
+    }
+    if (syntheticAssignment != null) {
+      syntheticAssignment = v.transform(syntheticAssignment);
+      syntheticAssignment?.parent = this;
+    }
+    if (expressionEffects != null) {
+      expressionEffects = v.transform(expressionEffects);
+      expressionEffects?.parent = this;
+    }
+    if (body != null) {
+      body = v.transform(body);
       body?.parent = this;
     }
   }
@@ -318,12 +342,25 @@
   @override
   void transformChildren(Transformer v) {
     if (tryBlock != null) {
-      tryBlock = tryBlock.accept<TreeNode>(v);
+      tryBlock = v.transform(tryBlock);
       tryBlock?.parent = this;
     }
-    transformList(catchBlocks, v, this);
+    v.transformList(catchBlocks, this);
     if (finallyBlock != null) {
-      finallyBlock = finallyBlock.accept<TreeNode>(v);
+      finallyBlock = v.transform(finallyBlock);
+      finallyBlock?.parent = this;
+    }
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    if (tryBlock != null) {
+      tryBlock = v.transformOrRemoveStatement(tryBlock);
+      tryBlock?.parent = this;
+    }
+    v.transformCatchList(catchBlocks, this);
+    if (finallyBlock != null) {
+      finallyBlock = v.transformOrRemoveStatement(finallyBlock);
       finallyBlock?.parent = this;
     }
   }
@@ -584,10 +621,19 @@
   @override
   void transformChildren(Transformer v) {
     if (variable != null) {
-      variable = variable.accept<TreeNode>(v);
+      variable = v.transform(variable);
       variable?.parent = this;
     }
-    transformList(expressions, v, this);
+    v.transformList(expressions, this);
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    if (variable != null) {
+      variable = v.transformOrRemoveVariableDeclaration(variable);
+      variable?.parent = this;
+    }
+    v.transformExpressionList(expressions, this);
   }
 
   @override
@@ -644,11 +690,23 @@
   @override
   void transformChildren(Transformer v) {
     if (variable != null) {
-      variable = variable.accept<TreeNode>(v);
+      variable = v.transform(variable);
       variable?.parent = this;
     }
     if (expression != null) {
-      expression = expression.accept<TreeNode>(v);
+      expression = v.transform(expression);
+      expression?.parent = this;
+    }
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    if (variable != null) {
+      variable = v.transformOrRemoveVariableDeclaration(variable);
+      variable?.parent = this;
+    }
+    if (expression != null) {
+      expression = v.transformOrRemoveExpression(expression);
       expression?.parent = this;
     }
   }
@@ -847,11 +905,23 @@
   @override
   void transformChildren(Transformer v) {
     if (left != null) {
-      left = left.accept<TreeNode>(v);
+      left = v.transform(left);
       left?.parent = this;
     }
     if (right != null) {
-      right = right.accept<TreeNode>(v);
+      right = v.transform(right);
+      right?.parent = this;
+    }
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    if (left != null) {
+      left = v.transformOrRemoveExpression(left);
+      left?.parent = this;
+    }
+    if (right != null) {
+      right = v.transformOrRemoveExpression(right);
       right?.parent = this;
     }
   }
@@ -1038,11 +1108,23 @@
   @override
   void transformChildren(Transformer v) {
     if (expression != null) {
-      expression = expression.accept<TreeNode>(v);
+      expression = v.transform(expression);
       expression?.parent = this;
     }
     if (arguments != null) {
-      arguments = arguments.accept<TreeNode>(v);
+      arguments = v.transform(arguments);
+      arguments?.parent = this;
+    }
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    if (expression != null) {
+      expression = v.transformOrRemoveExpression(expression);
+      expression?.parent = this;
+    }
+    if (arguments != null) {
+      arguments = v.transformOrRemove(arguments, dummyArguments);
       arguments?.parent = this;
     }
   }
@@ -1120,13 +1202,25 @@
   }
 
   @override
-  transformChildren(Transformer v) {
+  void transformChildren(Transformer v) {
     if (variable != null) {
-      variable = variable.accept<TreeNode>(v);
+      variable = v.transform(variable);
       variable?.parent = this;
     }
     if (invocation != null) {
-      invocation = invocation.accept<TreeNode>(v);
+      invocation = v.transform(invocation);
+      invocation?.parent = this;
+    }
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    if (variable != null) {
+      variable = v.transformOrRemoveVariableDeclaration(variable);
+      variable?.parent = this;
+    }
+    if (invocation != null) {
+      invocation = v.transformOrRemoveExpression(invocation);
       invocation?.parent = this;
     }
   }
@@ -1193,13 +1287,25 @@
   }
 
   @override
-  transformChildren(Transformer v) {
+  void transformChildren(Transformer v) {
     if (variable != null) {
-      variable = variable.accept<TreeNode>(v);
+      variable = v.transform(variable);
       variable?.parent = this;
     }
     if (read != null) {
-      read = read.accept<TreeNode>(v);
+      read = v.transform(read);
+      read?.parent = this;
+    }
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    if (variable != null) {
+      variable = v.transform(variable);
+      variable?.parent = this;
+    }
+    if (read != null) {
+      read = v.transform(read);
       read?.parent = this;
     }
   }
@@ -1265,13 +1371,25 @@
   }
 
   @override
-  transformChildren(Transformer v) {
+  void transformChildren(Transformer v) {
     if (variable != null) {
-      variable = variable.accept<TreeNode>(v);
+      variable = v.transform(variable);
       variable?.parent = this;
     }
     if (write != null) {
-      write = write.accept<TreeNode>(v);
+      write = v.transform(write);
+      write?.parent = this;
+    }
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    if (variable != null) {
+      variable = v.transformOrRemoveVariableDeclaration(variable);
+      variable?.parent = this;
+    }
+    if (write != null) {
+      write = v.transformOrRemoveExpression(write);
       write?.parent = this;
     }
   }
@@ -1508,6 +1626,11 @@
         isLate: isLate || lateGetter != null, type: lateType ?? type);
     printer.write(';');
   }
+
+  @override
+  String toString() {
+    return "VariableDeclarationImpl(${toStringInternal()})";
+  }
 }
 
 /// Front end specific implementation of [VariableGet].
@@ -1568,19 +1691,14 @@
 
   @override
   void visitChildren(Visitor<dynamic> v) {
-    import?.accept(v);
-    target?.accept(v);
+    v.visitProcedureReference(target);
   }
 
   @override
-  void transformChildren(Transformer v) {
-    if (import != null) {
-      import = import.accept<TreeNode>(v);
-    }
-    if (target != null) {
-      target = target.accept<TreeNode>(v);
-    }
-  }
+  void transformChildren(Transformer v) {}
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {}
 
   @override
   String toString() {
@@ -1649,11 +1767,23 @@
   @override
   void transformChildren(Transformer v) {
     if (receiver != null) {
-      receiver = receiver.accept<TreeNode>(v);
+      receiver = v.transform(receiver);
       receiver?.parent = this;
     }
     if (rhs != null) {
-      rhs = rhs.accept<TreeNode>(v);
+      rhs = v.transform(rhs);
+      rhs?.parent = this;
+    }
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    if (receiver != null) {
+      receiver = v.transformOrRemoveExpression(receiver);
+      receiver?.parent = this;
+    }
+    if (rhs != null) {
+      rhs = v.transformOrRemoveExpression(rhs);
       rhs?.parent = this;
     }
   }
@@ -1718,11 +1848,23 @@
   @override
   void transformChildren(Transformer v) {
     if (read != null) {
-      read = read.accept<TreeNode>(v);
+      read = v.transform(read);
       read?.parent = this;
     }
     if (write != null) {
-      write = write.accept<TreeNode>(v);
+      write = v.transform(write);
+      write?.parent = this;
+    }
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    if (read != null) {
+      read = v.transformOrRemoveExpression(read);
+      read?.parent = this;
+    }
+    if (write != null) {
+      write = v.transformOrRemoveExpression(write);
       write?.parent = this;
     }
   }
@@ -1841,11 +1983,23 @@
   @override
   void transformChildren(Transformer v) {
     if (receiver != null) {
-      receiver = receiver.accept<TreeNode>(v);
+      receiver = v.transform(receiver);
       receiver?.parent = this;
     }
     if (rhs != null) {
-      rhs = rhs.accept<TreeNode>(v);
+      rhs = v.transform(rhs);
+      rhs?.parent = this;
+    }
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    if (receiver != null) {
+      receiver = v.transformOrRemoveExpression(receiver);
+      receiver?.parent = this;
+    }
+    if (rhs != null) {
+      rhs = v.transformOrRemoveExpression(rhs);
       rhs?.parent = this;
     }
   }
@@ -1921,11 +2075,23 @@
   @override
   void transformChildren(Transformer v) {
     if (receiver != null) {
-      receiver = receiver.accept<TreeNode>(v);
+      receiver = v.transform(receiver);
       receiver?.parent = this;
     }
     if (rhs != null) {
-      rhs = rhs.accept<TreeNode>(v);
+      rhs = v.transform(rhs);
+      rhs?.parent = this;
+    }
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    if (receiver != null) {
+      receiver = v.transformOrRemoveExpression(receiver);
+      receiver?.parent = this;
+    }
+    if (rhs != null) {
+      rhs = v.transformOrRemoveExpression(rhs);
       rhs?.parent = this;
     }
   }
@@ -1997,11 +2163,23 @@
   @override
   void transformChildren(Transformer v) {
     if (variable != null) {
-      variable = variable.accept<TreeNode>(v);
+      variable = v.transform(variable);
       variable?.parent = this;
     }
     if (write != null) {
-      write = write.accept<TreeNode>(v);
+      write = v.transform(write);
+      write?.parent = this;
+    }
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    if (variable != null) {
+      variable = v.transformOrRemoveVariableDeclaration(variable);
+      variable?.parent = this;
+    }
+    if (write != null) {
+      write = v.transformOrRemoveVariableDeclaration(write);
       write?.parent = this;
     }
   }
@@ -2050,11 +2228,23 @@
   @override
   void transformChildren(Transformer v) {
     if (read != null) {
-      read = read.accept<TreeNode>(v);
+      read = v.transform(read);
       read?.parent = this;
     }
     if (write != null) {
-      write = write.accept<TreeNode>(v);
+      write = v.transform(write);
+      write?.parent = this;
+    }
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    if (read != null) {
+      read = v.transformOrRemoveVariableDeclaration(read);
+      read?.parent = this;
+    }
+    if (write != null) {
+      write = v.transformOrRemoveVariableDeclaration(write);
       write?.parent = this;
     }
   }
@@ -2103,11 +2293,23 @@
   @override
   void transformChildren(Transformer v) {
     if (read != null) {
-      read = read.accept<TreeNode>(v);
+      read = v.transform(read);
       read?.parent = this;
     }
     if (write != null) {
-      write = write.accept<TreeNode>(v);
+      write = v.transform(write);
+      write?.parent = this;
+    }
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    if (read != null) {
+      read = v.transform(read);
+      read?.parent = this;
+    }
+    if (write != null) {
+      write = v.transform(write);
       write?.parent = this;
     }
   }
@@ -2156,11 +2358,23 @@
   @override
   void transformChildren(Transformer v) {
     if (read != null) {
-      read = read.accept<TreeNode>(v);
+      read = v.transform(read);
       read?.parent = this;
     }
     if (write != null) {
-      write = write.accept<TreeNode>(v);
+      write = v.transform(write);
+      write?.parent = this;
+    }
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    if (read != null) {
+      read = v.transformOrRemoveVariableDeclaration(read);
+      read?.parent = this;
+    }
+    if (write != null) {
+      write = v.transformOrRemoveVariableDeclaration(write);
       write?.parent = this;
     }
   }
@@ -2202,11 +2416,23 @@
   @override
   void transformChildren(Transformer v) {
     if (receiver != null) {
-      receiver = receiver.accept<TreeNode>(v);
+      receiver = v.transform(receiver);
       receiver?.parent = this;
     }
     if (index != null) {
-      index = index.accept<TreeNode>(v);
+      index = v.transform(index);
+      index?.parent = this;
+    }
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    if (receiver != null) {
+      receiver = v.transformOrRemoveExpression(receiver);
+      receiver?.parent = this;
+    }
+    if (index != null) {
+      index = v.transformOrRemoveExpression(index);
       index?.parent = this;
     }
   }
@@ -2268,15 +2494,31 @@
   @override
   void transformChildren(Transformer v) {
     if (receiver != null) {
-      receiver = receiver.accept<TreeNode>(v);
+      receiver = v.transform(receiver);
       receiver?.parent = this;
     }
     if (index != null) {
-      index = index.accept<TreeNode>(v);
+      index = v.transform(index);
       index?.parent = this;
     }
     if (value != null) {
-      value = value.accept<TreeNode>(v);
+      value = v.transform(value);
+      value?.parent = this;
+    }
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    if (receiver != null) {
+      receiver = v.transformOrRemoveExpression(receiver);
+      receiver?.parent = this;
+    }
+    if (index != null) {
+      index = v.transformOrRemoveExpression(index);
+      index?.parent = this;
+    }
+    if (value != null) {
+      value = v.transformOrRemoveExpression(value);
       value?.parent = this;
     }
   }
@@ -2333,11 +2575,23 @@
   @override
   void transformChildren(Transformer v) {
     if (index != null) {
-      index = index.accept<TreeNode>(v);
+      index = v.transform(index);
       index?.parent = this;
     }
     if (value != null) {
-      value = value.accept<TreeNode>(v);
+      value = v.transform(value);
+      value?.parent = this;
+    }
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    if (index != null) {
+      index = v.transformOrRemoveExpression(index);
+      index?.parent = this;
+    }
+    if (value != null) {
+      value = v.transformOrRemoveExpression(value);
       value?.parent = this;
     }
   }
@@ -2414,15 +2668,31 @@
   @override
   void transformChildren(Transformer v) {
     if (receiver != null) {
-      receiver = receiver.accept<TreeNode>(v);
+      receiver = v.transform(receiver);
       receiver?.parent = this;
     }
     if (index != null) {
-      index = index.accept<TreeNode>(v);
+      index = v.transform(index);
       index?.parent = this;
     }
     if (value != null) {
-      value = value.accept<TreeNode>(v);
+      value = v.transform(value);
+      value?.parent = this;
+    }
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    if (receiver != null) {
+      receiver = v.transformOrRemoveExpression(receiver);
+      receiver?.parent = this;
+    }
+    if (index != null) {
+      index = v.transformOrRemoveExpression(index);
+      index?.parent = this;
+    }
+    if (value != null) {
+      value = v.transformOrRemoveExpression(value);
       value?.parent = this;
     }
   }
@@ -2522,15 +2792,31 @@
   @override
   void transformChildren(Transformer v) {
     if (receiver != null) {
-      receiver = receiver.accept<TreeNode>(v);
+      receiver = v.transform(receiver);
       receiver?.parent = this;
     }
     if (index != null) {
-      index = index.accept<TreeNode>(v);
+      index = v.transform(index);
       index?.parent = this;
     }
     if (value != null) {
-      value = value.accept<TreeNode>(v);
+      value = v.transform(value);
+      value?.parent = this;
+    }
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    if (receiver != null) {
+      receiver = v.transformOrRemoveExpression(receiver);
+      receiver?.parent = this;
+    }
+    if (index != null) {
+      index = v.transformOrRemoveExpression(index);
+      index?.parent = this;
+    }
+    if (value != null) {
+      value = v.transformOrRemoveExpression(value);
       value?.parent = this;
     }
   }
@@ -2613,11 +2899,23 @@
   @override
   void transformChildren(Transformer v) {
     if (index != null) {
-      index = index.accept<TreeNode>(v);
+      index = v.transform(index);
       index?.parent = this;
     }
     if (value != null) {
-      value = value.accept<TreeNode>(v);
+      value = v.transform(value);
+      value?.parent = this;
+    }
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    if (index != null) {
+      index = v.transformOrRemoveExpression(index);
+      index?.parent = this;
+    }
+    if (value != null) {
+      value = v.transformOrRemoveExpression(value);
       value?.parent = this;
     }
   }
@@ -2713,15 +3011,31 @@
   @override
   void transformChildren(Transformer v) {
     if (receiver != null) {
-      receiver = receiver.accept<TreeNode>(v);
+      receiver = v.transform(receiver);
       receiver?.parent = this;
     }
     if (index != null) {
-      index = index.accept<TreeNode>(v);
+      index = v.transform(index);
       index?.parent = this;
     }
     if (value != null) {
-      value = value.accept<TreeNode>(v);
+      value = v.transform(value);
+      value?.parent = this;
+    }
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    if (receiver != null) {
+      receiver = v.transformOrRemoveExpression(receiver);
+      receiver?.parent = this;
+    }
+    if (index != null) {
+      index = v.transformOrRemoveExpression(index);
+      index?.parent = this;
+    }
+    if (value != null) {
+      value = v.transformOrRemoveExpression(value);
       value?.parent = this;
     }
   }
@@ -2806,15 +3120,31 @@
   @override
   void transformChildren(Transformer v) {
     if (receiver != null) {
-      receiver = receiver.accept<TreeNode>(v);
+      receiver = v.transform(receiver);
       receiver?.parent = this;
     }
     if (index != null) {
-      index = index.accept<TreeNode>(v);
+      index = v.transform(index);
       index?.parent = this;
     }
     if (rhs != null) {
-      rhs = rhs.accept<TreeNode>(v);
+      rhs = v.transform(rhs);
+      rhs?.parent = this;
+    }
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    if (receiver != null) {
+      receiver = v.transformOrRemoveExpression(receiver);
+      receiver?.parent = this;
+    }
+    if (index != null) {
+      index = v.transformOrRemoveExpression(index);
+      index?.parent = this;
+    }
+    if (rhs != null) {
+      rhs = v.transformOrRemoveExpression(rhs);
       rhs?.parent = this;
     }
   }
@@ -2945,11 +3275,23 @@
   @override
   void transformChildren(Transformer v) {
     if (receiver != null) {
-      receiver = receiver.accept<TreeNode>(v);
+      receiver = v.transform(receiver);
       receiver?.parent = this;
     }
     if (rhs != null) {
-      rhs = rhs.accept<TreeNode>(v);
+      rhs = v.transform(rhs);
+      rhs?.parent = this;
+    }
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    if (receiver != null) {
+      receiver = v.transformOrRemoveExpression(receiver);
+      receiver?.parent = this;
+    }
+    if (rhs != null) {
+      rhs = v.transformOrRemoveExpression(rhs);
       rhs?.parent = this;
     }
   }
@@ -3054,11 +3396,23 @@
   @override
   void transformChildren(Transformer v) {
     if (receiver != null) {
-      receiver = receiver.accept<TreeNode>(v);
+      receiver = v.transform(receiver);
       receiver?.parent = this;
     }
     if (value != null) {
-      value = value.accept<TreeNode>(v);
+      value = v.transform(value);
+      value?.parent = this;
+    }
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    if (receiver != null) {
+      receiver = v.transformOrRemoveExpression(receiver);
+      receiver?.parent = this;
+    }
+    if (value != null) {
+      value = v.transformOrRemoveExpression(value);
       value?.parent = this;
     }
   }
@@ -3154,11 +3508,23 @@
   @override
   void transformChildren(Transformer v) {
     if (index != null) {
-      index = index.accept<TreeNode>(v);
+      index = v.transform(index);
       index?.parent = this;
     }
     if (rhs != null) {
-      rhs = rhs.accept<TreeNode>(v);
+      rhs = v.transform(rhs);
+      rhs?.parent = this;
+    }
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    if (index != null) {
+      index = v.transformOrRemoveExpression(index);
+      index?.parent = this;
+    }
+    if (rhs != null) {
+      rhs = v.transformOrRemoveExpression(rhs);
       rhs?.parent = this;
     }
   }
@@ -3272,15 +3638,31 @@
   @override
   void transformChildren(Transformer v) {
     if (receiver != null) {
-      receiver = receiver.accept<TreeNode>(v);
+      receiver = v.transform(receiver);
       receiver?.parent = this;
     }
     if (index != null) {
-      index = index.accept<TreeNode>(v);
+      index = v.transform(index);
       index?.parent = this;
     }
     if (rhs != null) {
-      rhs = rhs.accept<TreeNode>(v);
+      rhs = v.transform(rhs);
+      rhs?.parent = this;
+    }
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    if (receiver != null) {
+      receiver = v.transformOrRemoveExpression(receiver);
+      receiver?.parent = this;
+    }
+    if (index != null) {
+      index = v.transformOrRemoveExpression(index);
+      index?.parent = this;
+    }
+    if (rhs != null) {
+      rhs = v.transformOrRemoveExpression(rhs);
       rhs?.parent = this;
     }
   }
@@ -3358,11 +3740,23 @@
   @override
   void transformChildren(Transformer v) {
     if (receiver != null) {
-      receiver = receiver.accept<TreeNode>(v);
+      receiver = v.transform(receiver);
       receiver?.parent = this;
     }
     if (value != null) {
-      value = value.accept<TreeNode>(v);
+      value = v.transform(value);
+      value?.parent = this;
+    }
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    if (receiver != null) {
+      receiver = v.transform(receiver);
+      receiver?.parent = this;
+    }
+    if (value != null) {
+      value = v.transform(value);
       value?.parent = this;
     }
   }
@@ -3409,11 +3803,23 @@
   @override
   void transformChildren(Transformer v) {
     if (variable != null) {
-      variable = variable.accept<TreeNode>(v);
+      variable = v.transform(variable);
       variable?.parent = this;
     }
     if (expression != null) {
-      expression = expression.accept<TreeNode>(v);
+      expression = v.transform(expression);
+      expression?.parent = this;
+    }
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    if (variable != null) {
+      variable = v.transformOrRemoveVariableDeclaration(variable);
+      variable?.parent = this;
+    }
+    if (expression != null) {
+      expression = v.transformOrRemoveExpression(expression);
       expression?.parent = this;
     }
   }
@@ -3484,7 +3890,15 @@
   @override
   void transformChildren(Transformer v) {
     if (arguments != null) {
-      arguments = arguments.accept<TreeNode>(v);
+      arguments = v.transform(arguments);
+      arguments?.parent = this;
+    }
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    if (arguments != null) {
+      arguments = v.transformOrRemove(arguments, dummyArguments);
       arguments?.parent = this;
     }
   }
@@ -3525,11 +3939,23 @@
   @override
   void transformChildren(Transformer v) {
     if (left != null) {
-      left = left.accept<TreeNode>(v);
+      left = v.transform(left);
       left?.parent = this;
     }
     if (right != null) {
-      right = right.accept<TreeNode>(v);
+      right = v.transform(right);
+      right?.parent = this;
+    }
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    if (left != null) {
+      left = v.transformOrRemoveExpression(left);
+      left?.parent = this;
+    }
+    if (right != null) {
+      right = v.transformOrRemoveExpression(right);
       right?.parent = this;
     }
   }
@@ -3580,11 +4006,23 @@
   @override
   void transformChildren(Transformer v) {
     if (left != null) {
-      left = left.accept<TreeNode>(v);
+      left = v.transform(left);
       left?.parent = this;
     }
     if (right != null) {
-      right = right.accept<TreeNode>(v);
+      right = v.transform(right);
+      right?.parent = this;
+    }
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    if (left != null) {
+      left = v.transformOrRemoveExpression(left);
+      left?.parent = this;
+    }
+    if (right != null) {
+      right = v.transformOrRemoveExpression(right);
       right?.parent = this;
     }
   }
@@ -3631,7 +4069,15 @@
   @override
   void transformChildren(Transformer v) {
     if (expression != null) {
-      expression = expression.accept<TreeNode>(v);
+      expression = v.transform(expression);
+      expression?.parent = this;
+    }
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    if (expression != null) {
+      expression = v.transformOrRemoveExpression(expression);
       expression?.parent = this;
     }
   }
@@ -3680,7 +4126,15 @@
   @override
   void transformChildren(Transformer v) {
     if (expression != null) {
-      expression = expression.accept<TreeNode>(v);
+      expression = v.transform(expression);
+      expression?.parent = this;
+    }
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    if (expression != null) {
+      expression = v.transformOrRemoveExpression(expression);
       expression?.parent = this;
     }
   }
diff --git a/pkg/front_end/lib/src/fasta/kernel/transform_collections.dart b/pkg/front_end/lib/src/fasta/kernel/transform_collections.dart
index 8c8545b..0866c33 100644
--- a/pkg/front_end/lib/src/fasta/kernel/transform_collections.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/transform_collections.dart
@@ -279,8 +279,8 @@
         element.condition?.accept<TreeNode>(this),
         element.updates,
         loopBody);
-    transformList(loop.variables, this, loop);
-    transformList(loop.updates, this, loop);
+    transformList(loop.variables, loop);
+    transformList(loop.updates, loop);
     _dataForTesting?.registerAlias(element, loop);
     body.add(loop);
   }
@@ -531,8 +531,8 @@
     ForStatement loop = _createForStatement(entry.fileOffset, entry.variables,
         entry.condition?.accept<TreeNode>(this), entry.updates, loopBody);
     _dataForTesting?.registerAlias(entry, loop);
-    transformList(loop.variables, this, loop);
-    transformList(loop.updates, this, loop);
+    transformList(loop.variables, loop);
+    transformList(loop.updates, loop);
     body.add(loop);
   }
 
diff --git a/pkg/front_end/test/spell_checking_list_code.txt b/pkg/front_end/test/spell_checking_list_code.txt
index 3858ee0..30beca7e 100644
--- a/pkg/front_end/test/spell_checking_list_code.txt
+++ b/pkg/front_end/test/spell_checking_list_code.txt
@@ -375,6 +375,7 @@
 enforce
 enforced
 enforces
+enforcing
 enumerates
 env
 eof
diff --git a/pkg/front_end/test/text_representation/empty_reference_test.dart b/pkg/front_end/test/text_representation/empty_reference_test.dart
index 22643b3..a33bd6b 100644
--- a/pkg/front_end/test/text_representation/empty_reference_test.dart
+++ b/pkg/front_end/test/text_representation/empty_reference_test.dart
@@ -85,7 +85,7 @@
 void testMembers() {
   testExpression(new PropertyGet(new IntLiteral(0), new Name('foo')), '''
 0.foo''');
-  testExpression(new StaticGet(null), '''
+  testExpression(new StaticGet.byReference(null), '''
 <missing-member-reference>''');
 
   Reference unlinkedMemberName = new Reference();
diff --git a/pkg/front_end/tool/_fasta/generate_experimental_flags.dart b/pkg/front_end/tool/_fasta/generate_experimental_flags.dart
index 5911b3a..c3ec736 100644
--- a/pkg/front_end/tool/_fasta/generate_experimental_flags.dart
+++ b/pkg/front_end/tool/_fasta/generate_experimental_flags.dart
@@ -63,8 +63,6 @@
 // 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.
 
-// @dart = 2.9
-
 // NOTE: THIS FILE IS GENERATED. DO NOT EDIT.
 //
 // Instead modify 'tools/experimental_features.yaml' and run
diff --git a/pkg/kernel/lib/ast.dart b/pkg/kernel/lib/ast.dart
index fa63366..fd0b0bc 100644
--- a/pkg/kernel/lib/ast.dart
+++ b/pkg/kernel/lib/ast.dart
@@ -2,8 +2,6 @@
 // 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.
 
-// @dart = 2.9
-
 /// -----------------------------------------------------------------------
 ///                          WHEN CHANGING THIS FILE:
 /// -----------------------------------------------------------------------
@@ -142,7 +140,7 @@
   final int hashCode = _hashCounter = (_hashCounter + 1) & 0x3fffffff;
   static const int noOffset = -1;
 
-  TreeNode parent;
+  TreeNode? parent;
 
   /// Offset in the source file it comes from.
   ///
@@ -151,8 +149,10 @@
   int fileOffset = noOffset;
 
   R accept<R>(TreeVisitor<R> v);
+  R accept1<R, A>(TreeVisitor1<R, A> v, A arg);
   void visitChildren(Visitor v);
   void transformChildren(Transformer v);
+  void transformOrRemoveChildren(RemovingTransformer v);
 
   /// Replaces [child] with [replacement].
   ///
@@ -164,6 +164,7 @@
   ///
   /// [replacement] must be non-null.
   void replaceChild(TreeNode child, TreeNode replacement) {
+    // ignore: unnecessary_null_comparison
     assert(replacement != null);
     transformChildren(new _ChildReplacer(child, replacement));
   }
@@ -176,24 +177,26 @@
   ///
   /// [replacement] must be non-null.
   void replaceWith(TreeNode replacement) {
+    // ignore: unnecessary_null_comparison
     assert(replacement != null);
-    parent.replaceChild(this, replacement);
+    parent!.replaceChild(this, replacement);
     parent = null;
   }
 
-  Component get enclosingComponent => parent?.enclosingComponent;
+  // TODO(johnniwinther): Make this non-nullable.
+  Component? get enclosingComponent => parent?.enclosingComponent;
 
   /// Returns the best known source location of the given AST node, or `null` if
   /// the node is orphaned.
   ///
   /// This getter is intended for diagnostics and debugging, and should be
   /// avoided in production code.
-  Location get location {
+  Location? get location {
     if (fileOffset == noOffset) return parent?.location;
     return _getLocationInEnclosingFile(fileOffset);
   }
 
-  Location _getLocationInEnclosingFile(int offset) {
+  Location? _getLocationInEnclosingFile(int offset) {
     return parent?._getLocationInEnclosingFile(offset);
   }
 }
@@ -205,17 +208,16 @@
 abstract class NamedNode extends TreeNode {
   final Reference reference;
 
-  NamedNode(Reference reference)
+  NamedNode(Reference? reference)
       : this.reference = reference ?? new Reference() {
     if (this is Field) {
-      Field me = this;
-      me.getterReference.node = this;
+      (this as Field).getterReference.node = this;
     } else {
       this.reference.node = this;
     }
   }
 
-  CanonicalName get canonicalName => reference?.canonicalName;
+  CanonicalName? get canonicalName => reference.canonicalName;
 
   /// This is an advanced feature.
   ///
@@ -229,7 +231,8 @@
 
 abstract class FileUriNode extends TreeNode {
   /// The URI of the source file this node was loaded from.
-  Uri get fileUri;
+  // TODO(johnniwinther): Make this non-nullable.
+  Uri? get fileUri;
 }
 
 abstract class Annotatable extends TreeNode {
@@ -241,17 +244,17 @@
 ///
 /// There is only one reference object per [NamedNode].
 class Reference {
-  CanonicalName canonicalName;
+  CanonicalName? canonicalName;
 
-  NamedNode _node;
+  NamedNode? _node;
 
-  NamedNode get node {
+  NamedNode? get node {
     if (_node == null) {
       // Either this is an unbound reference or it belongs to a lazy-loaded
       // (and not yet loaded) class. If it belongs to a lazy-loaded class,
       // load the class.
 
-      CanonicalName canonicalNameParent = canonicalName?.parent;
+      CanonicalName? canonicalNameParent = canonicalName?.parent;
       while (canonicalNameParent != null) {
         if (canonicalNameParent.name.startsWith("@")) {
           break;
@@ -259,8 +262,8 @@
         canonicalNameParent = canonicalNameParent.parent;
       }
       if (canonicalNameParent != null) {
-        NamedNode parentNamedNode =
-            canonicalNameParent?.parent?.reference?._node;
+        NamedNode? parentNamedNode =
+            canonicalNameParent.parent?.reference?._node;
         if (parentNamedNode is Class) {
           Class parentClass = parentNamedNode;
           if (parentClass.lazyBuilder != null) {
@@ -272,7 +275,7 @@
     return _node;
   }
 
-  void set node(NamedNode node) {
+  void set node(NamedNode? node) {
     _node = node;
   }
 
@@ -282,10 +285,10 @@
 
   String toStringInternal() {
     if (canonicalName != null) {
-      return '${canonicalName.toStringInternal()}';
+      return '${canonicalName!.toStringInternal()}';
     }
     if (node != null) {
-      return node.toStringInternal();
+      return node!.toStringInternal();
     }
     return 'Unbound reference';
   }
@@ -356,12 +359,14 @@
   Uri importUri;
 
   /// The URI of the source file this library was loaded from.
-  Uri fileUri;
+  @override
+  Uri? fileUri;
 
-  Version _languageVersion;
+  Version? _languageVersion;
   Version get languageVersion => _languageVersion ?? defaultLanguageVersion;
 
   void setLanguageVersion(Version languageVersion) {
+    // ignore: unnecessary_null_comparison
     if (languageVersion == null) {
       throw new StateError("Trying to set language version 'null'");
     }
@@ -421,14 +426,15 @@
     }
   }
 
-  String name;
+  String? name;
 
   /// Problems in this [Library] encoded as json objects.
   ///
   /// Note that this field can be null, and by convention should be null if the
   /// list is empty.
-  List<String> problemsAsJson;
+  List<String>? problemsAsJson;
 
+  @override
   final List<Expression> annotations;
 
   final List<LibraryDependency> dependencies;
@@ -449,16 +455,16 @@
 
   Library(this.importUri,
       {this.name,
-      List<Expression> annotations,
-      List<LibraryDependency> dependencies,
-      List<LibraryPart> parts,
-      List<Typedef> typedefs,
-      List<Class> classes,
-      List<Extension> extensions,
-      List<Procedure> procedures,
-      List<Field> fields,
+      List<Expression>? annotations,
+      List<LibraryDependency>? dependencies,
+      List<LibraryPart>? parts,
+      List<Typedef>? typedefs,
+      List<Class>? classes,
+      List<Extension>? extensions,
+      List<Procedure>? procedures,
+      List<Field>? fields,
       this.fileUri,
-      Reference reference})
+      Reference? reference})
       : this.annotations = annotations ?? <Expression>[],
         this.dependencies = dependencies ?? <LibraryDependency>[],
         this.parts = parts ?? <LibraryPart>[],
@@ -501,6 +507,7 @@
   Iterable<Member> get members =>
       <Iterable<Member>>[fields, procedures].expand((x) => x);
 
+  @override
   void addAnnotation(Expression node) {
     node.parent = this;
     annotations.add(node);
@@ -532,7 +539,7 @@
   }
 
   void computeCanonicalNames() {
-    assert(canonicalName != null);
+    CanonicalName canonicalName = this.canonicalName!;
     for (int i = 0; i < typedefs.length; ++i) {
       Typedef typedef_ = typedefs[i];
       canonicalName.getChildFromTypedef(typedef_).bindTo(typedef_.reference);
@@ -543,7 +550,7 @@
       if (field.hasSetter) {
         canonicalName
             .getChildFromFieldSetter(field)
-            .bindTo(field.setterReference);
+            .bindTo(field.setterReference!);
       }
     }
     for (int i = 0; i < procedures.length; ++i) {
@@ -552,12 +559,12 @@
     }
     for (int i = 0; i < classes.length; ++i) {
       Class class_ = classes[i];
-      canonicalName.getChild(class_.name).bindTo(class_.reference);
+      canonicalName.getChild(class_.name!).bindTo(class_.reference);
       class_.computeCanonicalNames();
     }
     for (int i = 0; i < extensions.length; ++i) {
       Extension extension = extensions[i];
-      canonicalName.getChild(extension.name).bindTo(extension.reference);
+      canonicalName.getChild(extension.name!).bindTo(extension.reference);
     }
   }
 
@@ -600,9 +607,14 @@
     parts.add(node..parent = this);
   }
 
+  @override
   R accept<R>(TreeVisitor<R> v) => v.visitLibrary(this);
 
-  visitChildren(Visitor v) {
+  @override
+  R accept1<R, A>(TreeVisitor1<R, A> v, A arg) => v.visitLibrary(this, arg);
+
+  @override
+  void visitChildren(Visitor v) {
     visitList(annotations, v);
     visitList(dependencies, v);
     visitList(parts, v);
@@ -613,20 +625,34 @@
     visitList(fields, v);
   }
 
-  transformChildren(Transformer v) {
-    transformList(annotations, v, this);
-    transformList(dependencies, v, this);
-    transformList(parts, v, this);
-    transformList(typedefs, v, this);
-    transformList(classes, v, this);
-    transformList(extensions, v, this);
-    transformList(procedures, v, this);
-    transformList(fields, v, this);
+  @override
+  void transformChildren(Transformer v) {
+    v.transformList(annotations, this);
+    v.transformList(dependencies, this);
+    v.transformList(parts, this);
+    v.transformList(typedefs, this);
+    v.transformList(classes, this);
+    v.transformList(extensions, this);
+    v.transformList(procedures, this);
+    v.transformList(fields, this);
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    v.transformExpressionList(annotations, this);
+    v.transformLibraryDependencyList(dependencies, this);
+    v.transformLibraryPartList(parts, this);
+    v.transformTypedefList(typedefs, this);
+    v.transformClassList(classes, this);
+    v.transformExtensionList(extensions, this);
+    v.transformProcedureList(procedures, this);
+    v.transformFieldList(fields, this);
   }
 
   static int _libraryIdCounter = 0;
   int _libraryId = ++_libraryIdCounter;
 
+  @override
   int compareTo(Library other) => _libraryId - other._libraryId;
 
   /// Returns a possibly synthesized name for this library, consistent with
@@ -639,7 +665,7 @@
     printer.write(libraryNameToString(this));
   }
 
-  Location _getLocationInEnclosingFile(int offset) {
+  Location? _getLocationInEnclosingFile(int offset) {
     return _getLocationInComponent(enclosingComponent, fileUri, offset);
   }
 
@@ -667,7 +693,7 @@
   /// with a prefix.
   ///
   /// Must be non-null for deferred imports, and must be null for exports.
-  String name;
+  String? name;
 
   final List<Combinator> combinators;
 
@@ -677,17 +703,19 @@
             flags, annotations, importedLibrary.reference, name, combinators);
 
   LibraryDependency.deferredImport(Library importedLibrary, String name,
-      {List<Combinator> combinators, List<Expression> annotations})
+      {List<Combinator>? combinators, List<Expression>? annotations})
       : this.byReference(DeferredFlag, annotations ?? <Expression>[],
             importedLibrary.reference, name, combinators ?? <Combinator>[]);
 
   LibraryDependency.import(Library importedLibrary,
-      {String name, List<Combinator> combinators, List<Expression> annotations})
+      {String? name,
+      List<Combinator>? combinators,
+      List<Expression>? annotations})
       : this.byReference(0, annotations ?? <Expression>[],
             importedLibrary.reference, name, combinators ?? <Combinator>[]);
 
   LibraryDependency.export(Library importedLibrary,
-      {List<Combinator> combinators, List<Expression> annotations})
+      {List<Combinator>? combinators, List<Expression>? annotations})
       : this.byReference(ExportFlag, annotations ?? <Expression>[],
             importedLibrary.reference, null, combinators ?? <Combinator>[]);
 
@@ -697,7 +725,7 @@
     setParents(combinators, this);
   }
 
-  Library get enclosingLibrary => parent;
+  Library get enclosingLibrary => parent as Library;
   Library get targetLibrary => importedLibraryReference.asLibrary;
 
   static const int ExportFlag = 1 << 0;
@@ -711,16 +739,29 @@
     annotations.add(annotation..parent = this);
   }
 
+  @override
   R accept<R>(TreeVisitor<R> v) => v.visitLibraryDependency(this);
 
-  visitChildren(Visitor v) {
+  @override
+  R accept1<R, A>(TreeVisitor1<R, A> v, A arg) =>
+      v.visitLibraryDependency(this, arg);
+
+  @override
+  void visitChildren(Visitor v) {
     visitList(annotations, v);
     visitList(combinators, v);
   }
 
-  transformChildren(Transformer v) {
-    transformList(annotations, v, this);
-    transformList(combinators, v, this);
+  @override
+  void transformChildren(Transformer v) {
+    v.transformList(annotations, this);
+    v.transformList(combinators, this);
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    v.transformExpressionList(annotations, this);
+    v.transformCombinatorList(combinators, this);
   }
 
   @override
@@ -751,14 +792,25 @@
     annotations.add(annotation..parent = this);
   }
 
+  @override
   R accept<R>(TreeVisitor<R> v) => v.visitLibraryPart(this);
 
-  visitChildren(Visitor v) {
+  @override
+  R accept1<R, A>(TreeVisitor1<R, A> v, A arg) => v.visitLibraryPart(this, arg);
+
+  @override
+  void visitChildren(Visitor v) {
     visitList(annotations, v);
   }
 
-  transformChildren(Transformer v) {
-    transformList(annotations, v, this);
+  @override
+  void transformChildren(Transformer v) {
+    v.transformList(annotations, this);
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    v.transformExpressionList(annotations, this);
   }
 
   @override
@@ -778,7 +830,7 @@
 
   final List<String> names;
 
-  LibraryDependency get dependency => parent;
+  LibraryDependency get dependency => parent as LibraryDependency;
 
   Combinator(this.isShow, this.names);
   Combinator.show(this.names) : isShow = true;
@@ -790,10 +842,16 @@
   R accept<R>(TreeVisitor<R> v) => v.visitCombinator(this);
 
   @override
-  visitChildren(Visitor v) {}
+  R accept1<R, A>(TreeVisitor1<R, A> v, A arg) => v.visitCombinator(this, arg);
 
   @override
-  transformChildren(Transformer v) {}
+  void visitChildren(Visitor v) {}
+
+  @override
+  void transformChildren(Transformer v) {}
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {}
 
   @override
   String toString() {
@@ -809,11 +867,13 @@
 /// Declaration of a type alias.
 class Typedef extends NamedNode implements FileUriNode {
   /// The URI of the source file that contains the declaration of this typedef.
-  Uri fileUri;
+  @override
+  Uri? fileUri;
+
   List<Expression> annotations = const <Expression>[];
   String name;
   final List<TypeParameter> typeParameters;
-  DartType type;
+  DartType? type;
 
   // The following two fields describe parameters of the underlying type when
   // that is a function type.  They are needed to keep such attributes as names
@@ -824,12 +884,12 @@
   final List<VariableDeclaration> namedParameters;
 
   Typedef(this.name, this.type,
-      {Reference reference,
+      {Reference? reference,
       this.fileUri,
-      List<TypeParameter> typeParameters,
-      List<TypeParameter> typeParametersOfFunctionType,
-      List<VariableDeclaration> positionalParameters,
-      List<VariableDeclaration> namedParameters})
+      List<TypeParameter>? typeParameters,
+      List<TypeParameter>? typeParametersOfFunctionType,
+      List<VariableDeclaration>? positionalParameters,
+      List<VariableDeclaration>? namedParameters})
       : this.typeParameters = typeParameters ?? <TypeParameter>[],
         this.typeParametersOfFunctionType =
             typeParametersOfFunctionType ?? <TypeParameter>[],
@@ -840,26 +900,44 @@
     setParents(this.typeParameters, this);
   }
 
-  Library get enclosingLibrary => parent;
+  Library get enclosingLibrary => parent as Library;
 
-  R accept<R>(TreeVisitor<R> v) {
-    return v.visitTypedef(this);
-  }
+  @override
+  R accept<R>(TreeVisitor<R> v) => v.visitTypedef(this);
 
-  transformChildren(Transformer v) {
-    transformList(annotations, v, this);
-    transformList(typeParameters, v, this);
-    if (type != null) {
-      type = v.visitDartType(type);
-    }
-  }
+  @override
+  R accept1<R, A>(TreeVisitor1<R, A> v, A arg) => v.visitTypedef(this, arg);
 
-  visitChildren(Visitor v) {
+  @override
+  void visitChildren(Visitor v) {
     visitList(annotations, v);
     visitList(typeParameters, v);
     type?.accept(v);
   }
 
+  @override
+  void transformChildren(Transformer v) {
+    v.transformList(annotations, this);
+    v.transformList(typeParameters, this);
+    if (type != null) {
+      type = v.visitDartType(type!);
+    }
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    v.transformExpressionList(annotations, this);
+    v.transformTypeParameterList(typeParameters, this);
+    if (type != null) {
+      DartType newType = v.visitDartType(type!, dummyDartType);
+      if (identical(newType, dummyDartType)) {
+        type = null;
+      } else {
+        type = newType;
+      }
+    }
+  }
+
   void addAnnotation(Expression node) {
     if (annotations.isEmpty) {
       annotations = <Expression>[];
@@ -868,8 +946,9 @@
     node.parent = this;
   }
 
-  Location _getLocationInEnclosingFile(int offset) {
-    return _getLocationInComponent(enclosingComponent, fileUri, offset);
+  @override
+  Location? _getLocationInEnclosingFile(int offset) {
+    return _getLocationInComponent(enclosingComponent, fileUri!, offset);
   }
 
   @override
@@ -943,6 +1022,7 @@
   ///
   /// This defaults to an immutable empty list. Use [addAnnotation] to add
   /// annotations if needed.
+  @override
   List<Expression> annotations = const <Expression>[];
 
   /// Name of the class.
@@ -952,7 +1032,8 @@
   /// The name may contain characters that are not valid in a Dart identifier,
   /// in particular, the symbol '&' is used in class names generated for mixin
   /// applications.
-  String name;
+  // TODO(johnniwinther): Make this non-nullable.
+  String? name;
 
   // Must match serialized bit positions.
   static const int FlagAbstract = 1 << 0;
@@ -1037,7 +1118,7 @@
     // Otherwise we have a left-linear binary tree (subtrees are supertype and
     // mixedInType) of constraints, where all the interior nodes are anonymous
     // mixin applications.
-    Supertype current = supertype;
+    Supertype? current = supertype;
     while (current != null && current.classNode.isAnonymousMixin) {
       Class currentClass = current.classNode;
       assert(currentClass.implementedTypes.length == 2);
@@ -1047,19 +1128,20 @@
       current =
           substitution.substituteSupertype(currentClass.implementedTypes[0]);
     }
-    return constraints..add(current);
+    return constraints..add(current!);
   }
 
   /// The URI of the source file this class was loaded from.
-  Uri fileUri;
+  @override
+  Uri? fileUri;
 
   final List<TypeParameter> typeParameters;
 
   /// The immediate super type, or `null` if this is the root class.
-  Supertype supertype;
+  Supertype? supertype;
 
   /// The mixed-in type if this is a mixin application, otherwise `null`.
-  Supertype mixedInType;
+  Supertype? mixedInType;
 
   /// The types from the `implements` clause.
   final List<Supertype> implementedTypes;
@@ -1069,14 +1151,14 @@
   /// If non-null, the function that will have to be called to fill-out the
   /// content of this class. Note that this should not be called directly
   /// though.
-  void Function() lazyBuilder;
+  void Function()? lazyBuilder;
 
   /// Makes sure the class is loaded, i.e. the fields, procedures etc have been
   /// loaded from the dill. Generally, one should not need to call this as it is
   /// done automatically when accessing the lists.
   void ensureLoaded() {
-    if (lazyBuilder != null) {
-      void Function() lazyBuilderLocal = lazyBuilder;
+    void Function()? lazyBuilderLocal = lazyBuilder;
+    if (lazyBuilderLocal != null) {
       lazyBuilder = null;
       lazyBuilderLocal();
     }
@@ -1086,7 +1168,7 @@
   ///
   /// Used for adding fields when reading the dill file.
   final List<Field> fieldsInternal;
-  DirtifyingList<Field> _fieldsView;
+  DirtifyingList<Field>? _fieldsView;
 
   /// Fields declared in the class.
   ///
@@ -1095,30 +1177,28 @@
     ensureLoaded();
     // If already dirty the caller just might as well add stuff directly too.
     if (dirty) return fieldsInternal;
-    _fieldsView ??= new DirtifyingList(this, fieldsInternal);
-    return _fieldsView;
+    return _fieldsView ??= new DirtifyingList(this, fieldsInternal);
   }
 
   /// Internal. Should *ONLY* be used from within kernel.
   ///
   /// Used for adding constructors when reading the dill file.
   final List<Constructor> constructorsInternal;
-  DirtifyingList<Constructor> _constructorsView;
+  DirtifyingList<Constructor>? _constructorsView;
 
   /// Constructors declared in the class.
   List<Constructor> get constructors {
     ensureLoaded();
     // If already dirty the caller just might as well add stuff directly too.
     if (dirty) return constructorsInternal;
-    _constructorsView ??= new DirtifyingList(this, constructorsInternal);
-    return _constructorsView;
+    return _constructorsView ??= new DirtifyingList(this, constructorsInternal);
   }
 
   /// Internal. Should *ONLY* be used from within kernel.
   ///
   /// Used for adding procedures when reading the dill file.
   final List<Procedure> proceduresInternal;
-  DirtifyingList<Procedure> _proceduresView;
+  DirtifyingList<Procedure>? _proceduresView;
 
   /// Procedures declared in the class.
   ///
@@ -1127,8 +1207,7 @@
     ensureLoaded();
     // If already dirty the caller just might as well add stuff directly too.
     if (dirty) return proceduresInternal;
-    _proceduresView ??= new DirtifyingList(this, proceduresInternal);
-    return _proceduresView;
+    return _proceduresView ??= new DirtifyingList(this, proceduresInternal);
   }
 
   /// Internal. Should *ONLY* be used from within kernel.
@@ -1137,7 +1216,7 @@
   /// file.
   final List<RedirectingFactoryConstructor>
       redirectingFactoryConstructorsInternal;
-  DirtifyingList<RedirectingFactoryConstructor>
+  DirtifyingList<RedirectingFactoryConstructor>?
       _redirectingFactoryConstructorsView;
 
   /// Redirecting factory constructors declared in the class.
@@ -1147,9 +1226,8 @@
     ensureLoaded();
     // If already dirty the caller just might as well add stuff directly too.
     if (dirty) return redirectingFactoryConstructorsInternal;
-    _redirectingFactoryConstructorsView ??=
+    return _redirectingFactoryConstructorsView ??=
         new DirtifyingList(this, redirectingFactoryConstructorsInternal);
-    return _redirectingFactoryConstructorsView;
   }
 
   Class(
@@ -1158,14 +1236,14 @@
       bool isAnonymousMixin: false,
       this.supertype,
       this.mixedInType,
-      List<TypeParameter> typeParameters,
-      List<Supertype> implementedTypes,
-      List<Constructor> constructors,
-      List<Procedure> procedures,
-      List<Field> fields,
-      List<RedirectingFactoryConstructor> redirectingFactoryConstructors,
+      List<TypeParameter>? typeParameters,
+      List<Supertype>? implementedTypes,
+      List<Constructor>? constructors,
+      List<Procedure>? procedures,
+      List<Field>? fields,
+      List<RedirectingFactoryConstructor>? redirectingFactoryConstructors,
       this.fileUri,
-      Reference reference})
+      Reference? reference})
       : this.typeParameters = typeParameters ?? <TypeParameter>[],
         this.implementedTypes = implementedTypes ?? <Supertype>[],
         this.fieldsInternal = fields ?? <Field>[],
@@ -1184,7 +1262,7 @@
   }
 
   void computeCanonicalNames() {
-    assert(canonicalName != null);
+    CanonicalName canonicalName = this.canonicalName!;
     if (!dirty) return;
     for (int i = 0; i < fields.length; ++i) {
       Field member = fields[i];
@@ -1192,7 +1270,7 @@
       if (member.hasSetter) {
         canonicalName
             .getChildFromFieldSetter(member)
-            .bindTo(member.setterReference);
+            .bindTo(member.setterReference!);
       }
     }
     for (int i = 0; i < procedures.length; ++i) {
@@ -1241,13 +1319,13 @@
   }
 
   /// The immediate super class, or `null` if this is the root class.
-  Class get superclass => supertype?.classNode;
+  Class? get superclass => supertype?.classNode;
 
   /// The mixed-in class if this is a mixin application, otherwise `null`.
   ///
   /// Note that this may itself be a mixin application.  Use [mixin] to get the
   /// class that has the fields and procedures.
-  Class get mixedInClass => mixedInType?.classNode;
+  Class? get mixedInClass => mixedInType?.classNode;
 
   /// The class that declares the field and procedures of this class.
   Class get mixin => mixedInClass?.mixin ?? this;
@@ -1256,18 +1334,18 @@
 
   String get demangledName {
     if (isAnonymousMixin) return nameAsMixinApplication;
-    assert(!name.contains('&'));
-    return name;
+    assert(!name!.contains('&'));
+    return name!;
   }
 
   String get nameAsMixinApplication {
     assert(isAnonymousMixin);
-    return demangleMixinApplicationName(name);
+    return demangleMixinApplicationName(name!);
   }
 
   String get nameAsMixinApplicationSubclass {
     assert(isAnonymousMixin);
-    return demangleMixinApplicationSubclassName(name);
+    return demangleMixinApplicationSubclassName(name!);
   }
 
   /// Members declared in this class.
@@ -1286,13 +1364,13 @@
   /// This getter is for convenience, not efficiency.  Consider manually
   /// iterating the super types to speed up code in production.
   Iterable<Supertype> get supers => <Iterable<Supertype>>[
-        supertype == null ? const [] : [supertype],
-        mixedInType == null ? const [] : [mixedInType],
+        supertype == null ? const [] : [supertype!],
+        mixedInType == null ? const [] : [mixedInType!],
         implementedTypes
       ].expand((x) => x);
 
   /// The library containing this class.
-  Library get enclosingLibrary => parent;
+  Library get enclosingLibrary => parent as Library;
 
   /// Internal. Should *ONLY* be used from within kernel.
   ///
@@ -1329,6 +1407,7 @@
     redirectingFactoryConstructorsInternal.add(redirectingFactoryConstructor);
   }
 
+  @override
   void addAnnotation(Expression node) {
     if (annotations.isEmpty) {
       annotations = <Expression>[];
@@ -1337,7 +1416,12 @@
     node.parent = this;
   }
 
+  @override
   R accept<R>(TreeVisitor<R> v) => v.visitClass(this);
+
+  @override
+  R accept1<R, A>(TreeVisitor1<R, A> v, A arg) => v.visitClass(this, arg);
+
   R acceptReference<R>(Visitor<R> v) => v.visitClassReference(this);
 
   Supertype get asRawSupertype {
@@ -1363,7 +1447,8 @@
     printer.writeClassName(reference);
   }
 
-  visitChildren(Visitor v) {
+  @override
+  void visitChildren(Visitor v) {
     visitList(annotations, v);
     visitList(typeParameters, v);
     supertype?.accept(v);
@@ -1375,24 +1460,54 @@
     visitList(redirectingFactoryConstructors, v);
   }
 
-  transformChildren(Transformer v) {
-    transformList(annotations, v, this);
-    transformList(typeParameters, v, this);
+  @override
+  void transformChildren(Transformer v) {
+    v.transformList(annotations, this);
+    v.transformList(typeParameters, this);
     if (supertype != null) {
-      supertype = v.visitSupertype(supertype);
+      supertype = v.visitSupertype(supertype!);
     }
     if (mixedInType != null) {
-      mixedInType = v.visitSupertype(mixedInType);
+      mixedInType = v.visitSupertype(mixedInType!);
     }
-    transformSupertypeList(implementedTypes, v);
-    transformList(constructors, v, this);
-    transformList(procedures, v, this);
-    transformList(fields, v, this);
-    transformList(redirectingFactoryConstructors, v, this);
+    v.transformSupertypeList(implementedTypes);
+    v.transformList(constructors, this);
+    v.transformList(procedures, this);
+    v.transformList(fields, this);
+    v.transformList(redirectingFactoryConstructors, this);
   }
 
-  Location _getLocationInEnclosingFile(int offset) {
-    return _getLocationInComponent(enclosingComponent, fileUri, offset);
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    v.transformExpressionList(annotations, this);
+    v.transformTypeParameterList(typeParameters, this);
+    if (supertype != null) {
+      Supertype newSupertype = v.visitSupertype(supertype!, dummySupertype);
+      if (identical(newSupertype, dummySupertype)) {
+        supertype = null;
+      } else {
+        supertype = newSupertype;
+      }
+    }
+    if (mixedInType != null) {
+      Supertype newMixedInType = v.visitSupertype(mixedInType!, dummySupertype);
+      if (identical(newMixedInType, dummySupertype)) {
+        mixedInType = null;
+      } else {
+        mixedInType = newMixedInType;
+      }
+    }
+    v.transformSupertypeList(implementedTypes);
+    v.transformConstructorList(constructors, this);
+    v.transformProcedureList(procedures, this);
+    v.transformFieldList(fields, this);
+    v.transformRedirectingFactoryConstructorList(
+        redirectingFactoryConstructors, this);
+  }
+
+  @override
+  Location? _getLocationInEnclosingFile(int offset) {
+    return _getLocationInComponent(enclosingComponent, fileUri!, offset);
   }
 }
 
@@ -1405,10 +1520,11 @@
   ///
   /// If unnamed, the extension will be given a synthesized name by the
   /// front end.
-  String name;
+  // TODO(johnniwinther): Make this non-nullable.
+  String? name;
 
   /// The URI of the source file this class was loaded from.
-  Uri fileUri;
+  Uri? fileUri;
 
   /// Type parameters declared on the extension.
   final List<TypeParameter> typeParameters;
@@ -1420,7 +1536,8 @@
   ///   class A {}
   ///   extension B on A {}
   ///
-  DartType onType;
+  // TODO(johnniwinther): Should this be late non-nullable?
+  DartType? onType;
 
   /// The members declared by the extension.
   ///
@@ -1430,39 +1547,55 @@
 
   Extension(
       {this.name,
-      List<TypeParameter> typeParameters,
+      List<TypeParameter>? typeParameters,
       this.onType,
-      List<ExtensionMemberDescriptor> members,
+      List<ExtensionMemberDescriptor>? members,
       this.fileUri,
-      Reference reference})
+      Reference? reference})
       : this.typeParameters = typeParameters ?? <TypeParameter>[],
         this.members = members ?? <ExtensionMemberDescriptor>[],
         super(reference) {
     setParents(this.typeParameters, this);
   }
 
-  Library get enclosingLibrary => parent;
+  Library get enclosingLibrary => parent as Library;
 
   @override
   R accept<R>(TreeVisitor<R> v) => v.visitExtension(this);
 
   @override
-  visitChildren(Visitor v) {
+  R accept1<R, A>(TreeVisitor1<R, A> v, A arg) => v.visitExtension(this, arg);
+
+  @override
+  void visitChildren(Visitor v) {
     visitList(typeParameters, v);
     onType?.accept(v);
   }
 
   @override
-  transformChildren(Transformer v) {
-    transformList(typeParameters, v, this);
+  void transformChildren(Transformer v) {
+    v.transformList(typeParameters, this);
     if (onType != null) {
-      onType = v.visitDartType(onType);
+      onType = v.visitDartType(onType!);
     }
   }
 
   @override
-  Location _getLocationInEnclosingFile(int offset) {
-    return _getLocationInComponent(enclosingComponent, fileUri, offset);
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    v.transformTypeParameterList(typeParameters, this);
+    if (onType != null) {
+      DartType newOnType = v.visitDartType(onType!, dummyDartType);
+      if (identical(newOnType, dummyDartType)) {
+        onType = null;
+      } else {
+        onType = newOnType;
+      }
+    }
+  }
+
+  @override
+  Location? _getLocationInEnclosingFile(int offset) {
+    return _getLocationInComponent(enclosingComponent, fileUri!, offset);
   }
 
   @override
@@ -1521,10 +1654,13 @@
   int flags = 0;
 
   /// Reference to the top-level member created for the extension method.
-  Reference member;
+  final Reference member;
 
   ExtensionMemberDescriptor(
-      {this.name, this.kind, bool isStatic: false, this.member}) {
+      {required this.name,
+      required this.kind,
+      bool isStatic: false,
+      required this.member}) {
     this.isStatic = isStatic;
   }
 
@@ -1558,12 +1694,15 @@
   ///
   /// This defaults to an immutable empty list. Use [addAnnotation] to add
   /// annotations if needed.
+  @override
   List<Expression> annotations = const <Expression>[];
 
-  Name name;
+  // TODO(johnniwinther): Make this non-nullable.
+  Name? name;
 
   /// The URI of the source file this member was loaded from.
-  Uri fileUri;
+  @override
+  Uri? fileUri;
 
   /// Flags summarizing the kinds of AST nodes contained in this member, for
   /// speeding up transformations that only affect certain types of nodes.
@@ -1582,13 +1721,19 @@
   // TODO(asgerf): It might be worthwhile to put this on classes as well.
   int transformerFlags = 0;
 
-  Member(this.name, this.fileUri, Reference reference) : super(reference);
+  Member(this.name, this.fileUri, Reference? reference) : super(reference);
 
-  Class get enclosingClass => parent is Class ? parent : null;
-  Library get enclosingLibrary => parent is Class ? parent.parent : parent;
+  Class? get enclosingClass => parent is Class ? parent as Class : null;
+  Library get enclosingLibrary =>
+      (parent is Class ? parent!.parent : parent) as Library;
 
+  @override
   R accept<R>(MemberVisitor<R> v);
-  acceptReference(MemberReferenceVisitor v);
+
+  @override
+  R accept1<R, A>(MemberVisitor1<R, A> v, A arg);
+
+  R acceptReference<R>(MemberReferenceVisitor<R> v);
 
   /// Returns true if this is an abstract procedure.
   bool get isAbstract => false;
@@ -1635,8 +1780,9 @@
   bool get isNonNullableByDefault;
   void set isNonNullableByDefault(bool value);
 
-  /// The body of the procedure or constructor, or `null` if this is a field.
-  FunctionNode get function => null;
+  /// The function signature and body of the procedure or constructor, or `null`
+  /// if this is a field.
+  FunctionNode? get function => null;
 
   /// Returns a possibly synthesized name for this member, consistent with
   /// the names used across all [toString] calls.
@@ -1648,6 +1794,7 @@
     printer.writeMemberName(reference);
   }
 
+  @override
   void addAnnotation(Expression node) {
     if (annotations.isEmpty) {
       annotations = <Expression>[];
@@ -1665,7 +1812,7 @@
 
   /// If this member is a member signature, [memberSignatureOrigin] is one of
   /// the non-member signature members from which it was created.
-  Member get memberSignatureOrigin => null;
+  Member? get memberSignatureOrigin => null;
 }
 
 /// A field declaration.
@@ -1675,17 +1822,22 @@
 class Field extends Member {
   DartType type; // Not null. Defaults to DynamicType.
   int flags = 0;
-  Expression initializer; // May be null.
-  final Reference setterReference;
+  Expression? initializer; // May be null.
+  final Reference? setterReference;
+
+  @override
   @Deprecated("Use the specific getterReference/setterReference instead")
   Reference get reference => super.reference;
 
   Reference get getterReference => super.reference;
+
+  @override
   @Deprecated(
       "Use the specific getterCanonicalName/setterCanonicalName instead")
-  CanonicalName get canonicalName => reference?.canonicalName;
-  CanonicalName get getterCanonicalName => getterReference?.canonicalName;
-  CanonicalName get setterCanonicalName => setterReference?.canonicalName;
+  CanonicalName? get canonicalName => reference.canonicalName;
+
+  CanonicalName? get getterCanonicalName => getterReference.canonicalName;
+  CanonicalName? get setterCanonicalName => setterReference?.canonicalName;
 
   Field.mutable(Name name,
       {this.type: const DynamicType(),
@@ -1695,12 +1847,13 @@
       bool isStatic: false,
       bool isLate: false,
       int transformerFlags: 0,
-      Uri fileUri,
-      Reference getterReference,
-      Reference setterReference})
+      Uri? fileUri,
+      Reference? getterReference,
+      Reference? setterReference})
       : this.setterReference = setterReference ?? new Reference(),
         super(name, fileUri, getterReference) {
-    this.setterReference.node = this;
+    this.setterReference!.node = this;
+    // ignore: unnecessary_null_comparison
     assert(type != null);
     initializer?.parent = this;
     this.isCovariant = isCovariant;
@@ -1719,10 +1872,11 @@
       bool isStatic: false,
       bool isLate: false,
       int transformerFlags: 0,
-      Uri fileUri,
-      Reference getterReference})
+      Uri? fileUri,
+      Reference? getterReference})
       : this.setterReference = null,
         super(name, fileUri, getterReference) {
+    // ignore: unnecessary_null_comparison
     assert(type != null);
     initializer?.parent = this;
     this.isCovariant = isCovariant;
@@ -1737,7 +1891,7 @@
   void _relinkNode() {
     super._relinkNode();
     if (hasSetter) {
-      this.setterReference.node = this;
+      this.setterReference!.node = this;
     }
   }
 
@@ -1755,7 +1909,10 @@
   bool get isCovariant => flags & FlagCovariant != 0;
 
   bool get isFinal => flags & FlagFinal != 0;
+
+  @override
   bool get isConst => flags & FlagConst != 0;
+
   bool get isStatic => flags & FlagStatic != 0;
 
   @override
@@ -1815,11 +1972,19 @@
         : (flags & ~FlagInternalImplementation);
   }
 
+  @override
   bool get isInstanceMember => !isStatic;
+
+  @override
   bool get hasGetter => true;
+
+  @override
   bool get hasSetter => setterReference != null;
 
+  @override
   bool get isExternal => false;
+
+  @override
   void set isExternal(bool value) {
     if (value) throw 'Fields cannot be external';
   }
@@ -1834,31 +1999,53 @@
         : (flags & ~FlagNonNullableByDefault);
   }
 
+  @override
   R accept<R>(MemberVisitor<R> v) => v.visitField(this);
 
-  acceptReference(MemberReferenceVisitor v) => v.visitFieldReference(this);
+  @override
+  R accept1<R, A>(MemberVisitor1<R, A> v, A arg) => v.visitField(this, arg);
 
-  visitChildren(Visitor v) {
+  @override
+  R acceptReference<R>(MemberReferenceVisitor<R> v) =>
+      v.visitFieldReference(this);
+
+  @override
+  void visitChildren(Visitor v) {
     visitList(annotations, v);
-    type?.accept(v);
+    type.accept(v);
     name?.accept(v);
     initializer?.accept(v);
   }
 
-  transformChildren(Transformer v) {
+  @override
+  void transformChildren(Transformer v) {
     type = v.visitDartType(type);
-    transformList(annotations, v, this);
+    v.transformList(annotations, this);
     if (initializer != null) {
-      initializer = initializer.accept<TreeNode>(v);
+      initializer = v.transform(initializer!);
       initializer?.parent = this;
     }
   }
 
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    type = v.visitDartType(type, null);
+    v.transformExpressionList(annotations, this);
+    if (initializer != null) {
+      initializer = v.transformOrRemoveExpression(initializer!);
+      initializer?.parent = this;
+    }
+  }
+
+  @override
   DartType get getterType => type;
+
+  @override
   DartType get setterType => hasSetter ? type : const BottomType();
 
-  Location _getLocationInEnclosingFile(int offset) {
-    return _getLocationInComponent(enclosingComponent, fileUri, offset);
+  @override
+  Location? _getLocationInEnclosingFile(int offset) {
+    return _getLocationInComponent(enclosingComponent, fileUri!, offset);
   }
 
   @override
@@ -1886,18 +2073,22 @@
   int startFileOffset = TreeNode.noOffset;
 
   int flags = 0;
-  FunctionNode function;
+
+  // TODO(johnniwinther): Make this non-nullable.
+  @override
+  FunctionNode? function;
+
   List<Initializer> initializers;
 
   Constructor(this.function,
-      {Name name,
+      {Name? name,
       bool isConst: false,
       bool isExternal: false,
       bool isSynthetic: false,
-      List<Initializer> initializers,
+      List<Initializer>? initializers,
       int transformerFlags: 0,
-      Uri fileUri,
-      Reference reference})
+      Uri? fileUri,
+      Reference? reference})
       : this.initializers = initializers ?? <Initializer>[],
         super(name, fileUri, reference) {
     function?.parent = this;
@@ -1913,7 +2104,10 @@
   static const int FlagSynthetic = 1 << 2;
   static const int FlagNonNullableByDefault = 1 << 3;
 
+  @override
   bool get isConst => flags & FlagConst != 0;
+
+  @override
   bool get isExternal => flags & FlagExternal != 0;
 
   /// True if this is a synthetic constructor inserted in a class that
@@ -1924,6 +2118,7 @@
     flags = value ? (flags | FlagConst) : (flags & ~FlagConst);
   }
 
+  @override
   void set isExternal(bool value) {
     flags = value ? (flags | FlagExternal) : (flags & ~FlagExternal);
   }
@@ -1932,8 +2127,13 @@
     flags = value ? (flags | FlagSynthetic) : (flags & ~FlagSynthetic);
   }
 
+  @override
   bool get isInstanceMember => false;
+
+  @override
   bool get hasGetter => false;
+
+  @override
   bool get hasSetter => false;
 
   @override
@@ -1949,32 +2149,54 @@
         : (flags & ~FlagNonNullableByDefault);
   }
 
+  @override
   R accept<R>(MemberVisitor<R> v) => v.visitConstructor(this);
 
-  acceptReference(MemberReferenceVisitor v) =>
+  @override
+  R accept1<R, A>(MemberVisitor1<R, A> v, A arg) =>
+      v.visitConstructor(this, arg);
+
+  @override
+  R acceptReference<R>(MemberReferenceVisitor<R> v) =>
       v.visitConstructorReference(this);
 
-  visitChildren(Visitor v) {
+  @override
+  void visitChildren(Visitor v) {
     visitList(annotations, v);
     name?.accept(v);
     visitList(initializers, v);
     function?.accept(v);
   }
 
-  transformChildren(Transformer v) {
-    transformList(annotations, v, this);
-    transformList(initializers, v, this);
+  @override
+  void transformChildren(Transformer v) {
+    v.transformList(annotations, this);
+    v.transformList(initializers, this);
     if (function != null) {
-      function = function.accept<TreeNode>(v);
+      function = v.transform(function!);
       function?.parent = this;
     }
   }
 
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    v.transformExpressionList(annotations, this);
+    v.transformInitializerList(initializers, this);
+    if (function != null) {
+      function = v.transformOrRemove(function!, dummyFunctionNode);
+      function?.parent = this;
+    }
+  }
+
+  @override
   DartType get getterType => const BottomType();
+
+  @override
   DartType get setterType => const BottomType();
 
-  Location _getLocationInEnclosingFile(int offset) {
-    return _getLocationInComponent(enclosingComponent, fileUri, offset);
+  @override
+  Location? _getLocationInEnclosingFile(int offset) {
+    return _getLocationInComponent(enclosingComponent, fileUri!, offset);
   }
 }
 
@@ -2009,7 +2231,7 @@
 
   /// Reference to the constructor or the factory that this
   /// [RedirectingFactoryConstructor] redirects to.
-  Reference targetReference;
+  Reference? targetReference;
 
   /// [typeParameters] are duplicates of the type parameters of the enclosing
   /// class.  Because [RedirectingFactoryConstructor]s aren't instance members,
@@ -2028,17 +2250,17 @@
   List<VariableDeclaration> namedParameters;
 
   RedirectingFactoryConstructor(this.targetReference,
-      {Name name,
+      {Name? name,
       bool isConst: false,
       bool isExternal: false,
       int transformerFlags: 0,
-      List<DartType> typeArguments,
-      List<TypeParameter> typeParameters,
-      List<VariableDeclaration> positionalParameters,
-      List<VariableDeclaration> namedParameters,
-      int requiredParameterCount,
-      Uri fileUri,
-      Reference reference})
+      List<DartType>? typeArguments,
+      List<TypeParameter>? typeParameters,
+      List<VariableDeclaration>? positionalParameters,
+      List<VariableDeclaration>? namedParameters,
+      int? requiredParameterCount,
+      Uri? fileUri,
+      Reference? reference})
       : this.typeArguments = typeArguments ?? <DartType>[],
         this.typeParameters = typeParameters ?? <TypeParameter>[],
         this.positionalParameters =
@@ -2059,19 +2281,28 @@
   static const int FlagExternal = 1 << 1;
   static const int FlagNonNullableByDefault = 1 << 2;
 
+  @override
   bool get isConst => flags & FlagConst != 0;
+
+  @override
   bool get isExternal => flags & FlagExternal != 0;
 
   void set isConst(bool value) {
     flags = value ? (flags | FlagConst) : (flags & ~FlagConst);
   }
 
+  @override
   void set isExternal(bool value) {
     flags = value ? (flags | FlagExternal) : (flags & ~FlagExternal);
   }
 
+  @override
   bool get isInstanceMember => false;
+
+  @override
   bool get hasGetter => false;
+
+  @override
   bool get hasSetter => false;
 
   @override
@@ -2089,36 +2320,54 @@
         : (flags & ~FlagNonNullableByDefault);
   }
 
-  Member get target => targetReference?.asMember;
+  Member? get target => targetReference?.asMember;
 
-  void set target(Member member) {
+  void set target(Member? member) {
     assert(member is Constructor ||
         (member is Procedure && member.kind == ProcedureKind.Factory));
     targetReference = getMemberReferenceGetter(member);
   }
 
+  @override
   R accept<R>(MemberVisitor<R> v) => v.visitRedirectingFactoryConstructor(this);
 
-  acceptReference(MemberReferenceVisitor v) =>
+  @override
+  R accept1<R, A>(MemberVisitor1<R, A> v, A arg) =>
+      v.visitRedirectingFactoryConstructor(this, arg);
+
+  @override
+  R acceptReference<R>(MemberReferenceVisitor<R> v) =>
       v.visitRedirectingFactoryConstructorReference(this);
 
-  visitChildren(Visitor v) {
+  @override
+  void visitChildren(Visitor v) {
     visitList(annotations, v);
     target?.acceptReference(v);
     visitList(typeArguments, v);
     name?.accept(v);
   }
 
-  transformChildren(Transformer v) {
-    transformList(annotations, v, this);
-    transformTypeList(typeArguments, v);
+  @override
+  void transformChildren(Transformer v) {
+    v.transformList(annotations, this);
+    v.transformDartTypeList(typeArguments);
   }
 
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    v.transformExpressionList(annotations, this);
+    v.transformDartTypeList(typeArguments);
+  }
+
+  @override
   DartType get getterType => const BottomType();
+
+  @override
   DartType get setterType => const BottomType();
 
-  Location _getLocationInEnclosingFile(int offset) {
-    return _getLocationInComponent(enclosingComponent, fileUri, offset);
+  @override
+  Location? _getLocationInEnclosingFile(int offset) {
+    return _getLocationInComponent(enclosingComponent, fileUri!, offset);
   }
 }
 
@@ -2333,12 +2582,15 @@
 
   final ProcedureKind kind;
   int flags = 0;
-  // function is null if and only if abstract, external.
-  FunctionNode function;
+
+  // TODO(johnniwinther): Make this non-nullable.
+  @override
+  FunctionNode? function;
 
   // The function node's body might be lazily loaded, meaning that this value
   // might not be set correctly yet. Make sure the body is loaded before
   // returning anything.
+  @override
   int get transformerFlags {
     function?.body;
     return super.transformerFlags;
@@ -2347,6 +2599,7 @@
   // The function node's body might be lazily loaded, meaning that this value
   // might get overwritten later (when the body is read). To avoid that read the
   // body now and only set the value afterwards.
+  @override
   void set transformerFlags(int newValue) {
     function?.body;
     super.transformerFlags = newValue;
@@ -2360,7 +2613,7 @@
   }
 
   ProcedureStubKind stubKind;
-  Reference stubTargetReference;
+  Reference? stubTargetReference;
 
   Procedure(Name name, ProcedureKind kind, FunctionNode function,
       {bool isAbstract: false,
@@ -2370,10 +2623,10 @@
       bool isExtensionMember: false,
       bool isSynthetic: false,
       int transformerFlags: 0,
-      Uri fileUri,
-      Reference reference,
+      Uri? fileUri,
+      Reference? reference,
       ProcedureStubKind stubKind: ProcedureStubKind.Regular,
-      Member stubTarget})
+      Member? stubTarget})
       : this._byReferenceRenamed(name, kind, function,
             isAbstract: isAbstract,
             isStatic: isStatic,
@@ -2396,10 +2649,11 @@
       bool isExtensionMember: false,
       bool isSynthetic: false,
       int transformerFlags: 0,
-      Uri fileUri,
-      Reference reference,
+      Uri? fileUri,
+      Reference? reference,
       this.stubKind: ProcedureStubKind.Regular,
       this.stubTargetReference})
+      // ignore: unnecessary_null_comparison
       : assert(kind != null),
         super(name, fileUri, reference) {
     function?.parent = this;
@@ -2430,11 +2684,16 @@
   static const int FlagSynthetic = 1 << 7;
 
   bool get isStatic => flags & FlagStatic != 0;
+
+  @override
   bool get isAbstract => flags & FlagAbstract != 0;
+
+  @override
   bool get isExternal => flags & FlagExternal != 0;
 
   /// True if this has the `const` modifier.  This is only possible for external
   /// constant factories, such as `String.fromEnvironment`.
+  @override
   bool get isConst => flags & FlagConst != 0;
 
   /// If set, this flag indicates that this function's implementation exists
@@ -2488,6 +2747,7 @@
     flags = value ? (flags | FlagAbstract) : (flags & ~FlagAbstract);
   }
 
+  @override
   void set isExternal(bool value) {
     flags = value ? (flags | FlagExternal) : (flags & ~FlagExternal);
   }
@@ -2511,12 +2771,19 @@
     flags = value ? (flags | FlagSynthetic) : (flags & ~FlagSynthetic);
   }
 
+  @override
   bool get isInstanceMember => !isStatic;
+
   bool get isGetter => kind == ProcedureKind.Getter;
   bool get isSetter => kind == ProcedureKind.Setter;
   bool get isAccessor => isGetter || isSetter;
+
+  @override
   bool get hasGetter => kind != ProcedureKind.Setter;
+
+  @override
   bool get hasSetter => kind == ProcedureKind.Setter;
+
   bool get isFactory => kind == ProcedureKind.Factory;
 
   @override
@@ -2529,59 +2796,80 @@
         : (flags & ~FlagNonNullableByDefault);
   }
 
-  Member get concreteForwardingStubTarget =>
+  Member? get concreteForwardingStubTarget =>
       stubKind == ProcedureStubKind.ConcreteForwardingStub
           ? stubTargetReference?.asMember
           : null;
 
-  Member get abstractForwardingStubTarget =>
+  Member? get abstractForwardingStubTarget =>
       stubKind == ProcedureStubKind.AbstractForwardingStub
           ? stubTargetReference?.asMember
           : null;
 
-  Member get stubTarget => stubTargetReference?.asMember;
+  Member? get stubTarget => stubTargetReference?.asMember;
 
-  void set stubTarget(Member target) {
+  void set stubTarget(Member? target) {
     stubTargetReference = getMemberReferenceBasedOnProcedureKind(target, kind);
   }
 
-  Member get memberSignatureOrigin =>
+  @override
+  Member? get memberSignatureOrigin =>
       stubKind == ProcedureStubKind.MemberSignature
           ? stubTargetReference?.asMember
           : null;
 
+  @override
   R accept<R>(MemberVisitor<R> v) => v.visitProcedure(this);
 
-  acceptReference(MemberReferenceVisitor v) => v.visitProcedureReference(this);
+  @override
+  R accept1<R, A>(MemberVisitor1<R, A> v, A arg) => v.visitProcedure(this, arg);
 
-  visitChildren(Visitor v) {
+  @override
+  R acceptReference<R>(MemberReferenceVisitor<R> v) =>
+      v.visitProcedureReference(this);
+
+  @override
+  void visitChildren(Visitor v) {
     visitList(annotations, v);
     name?.accept(v);
     function?.accept(v);
   }
 
-  transformChildren(Transformer v) {
-    transformList(annotations, v, this);
+  @override
+  void transformChildren(Transformer v) {
+    v.transformList(annotations, this);
     if (function != null) {
-      function = function.accept<TreeNode>(v);
+      function = v.transform(function!);
       function?.parent = this;
     }
   }
 
-  DartType get getterType {
-    return isGetter
-        ? function.returnType
-        : function.computeFunctionType(enclosingLibrary.nonNullable);
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    v.transformExpressionList(annotations, this);
+    if (function != null) {
+      function = v.transformOrRemove(function!, dummyFunctionNode);
+      function?.parent = this;
+    }
   }
 
+  @override
+  DartType get getterType {
+    return isGetter
+        ? function!.returnType
+        : function!.computeFunctionType(enclosingLibrary.nonNullable);
+  }
+
+  @override
   DartType get setterType {
     return isSetter
-        ? function.positionalParameters[0].type
+        ? function!.positionalParameters[0].type
         : const BottomType();
   }
 
-  Location _getLocationInEnclosingFile(int offset) {
-    return _getLocationInComponent(enclosingComponent, fileUri, offset);
+  @override
+  Location? _getLocationInEnclosingFile(int offset) {
+    return _getLocationInComponent(enclosingComponent, fileUri!, offset);
   }
 }
 
@@ -2603,7 +2891,11 @@
   @informative
   bool isSynthetic = false;
 
+  @override
   R accept<R>(InitializerVisitor<R> v);
+
+  @override
+  R accept1<R, A>(InitializerVisitor1<R, A> v, A arg);
 }
 
 /// An initializer with a compile-time error.
@@ -2613,10 +2905,21 @@
 // DESIGN TODO: The frontend should use this in a lot more cases to catch
 // invalid cases.
 class InvalidInitializer extends Initializer {
+  @override
   R accept<R>(InitializerVisitor<R> v) => v.visitInvalidInitializer(this);
 
-  visitChildren(Visitor v) {}
-  transformChildren(Transformer v) {}
+  @override
+  R accept1<R, A>(InitializerVisitor1<R, A> v, A arg) =>
+      v.visitInvalidInitializer(this, arg);
+
+  @override
+  void visitChildren(Visitor v) {}
+
+  @override
+  void transformChildren(Transformer v) {}
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {}
 
   @override
   String toString() {
@@ -2645,30 +2948,47 @@
   FieldInitializer(Field field, Expression value)
       : this.byReference(
             // getterReference is used since this refers to the field itself
-            field?.getterReference,
+            field.getterReference,
             value);
 
   FieldInitializer.byReference(this.fieldReference, this.value) {
-    value?.parent = this;
+    value.parent = this;
   }
 
-  Field get field => fieldReference?.node;
+  Field get field => fieldReference.asField;
 
   void set field(Field field) {
-    fieldReference = field?.getterReference;
+    fieldReference = field.getterReference;
   }
 
+  @override
   R accept<R>(InitializerVisitor<R> v) => v.visitFieldInitializer(this);
 
-  visitChildren(Visitor v) {
-    field?.acceptReference(v);
-    value?.accept(v);
+  @override
+  R accept1<R, A>(InitializerVisitor1<R, A> v, A arg) =>
+      v.visitFieldInitializer(this, arg);
+
+  @override
+  void visitChildren(Visitor v) {
+    field.acceptReference(v);
+    value.accept(v);
   }
 
-  transformChildren(Transformer v) {
+  @override
+  void transformChildren(Transformer v) {
+    // ignore: unnecessary_null_comparison
     if (value != null) {
-      value = value.accept<TreeNode>(v);
-      value?.parent = this;
+      value = v.transform(value);
+      value.parent = this;
+    }
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    // ignore: unnecessary_null_comparison
+    if (value != null) {
+      value = v.transform(value);
+      value.parent = this;
     }
   }
 
@@ -2700,31 +3020,48 @@
   SuperInitializer(Constructor target, Arguments arguments)
       : this.byReference(
             // Getter vs setter doesn't matter for constructors.
-            getMemberReferenceGetter(target),
+            getNonNullableMemberReferenceGetter(target),
             arguments);
 
   SuperInitializer.byReference(this.targetReference, this.arguments) {
-    arguments?.parent = this;
+    arguments.parent = this;
   }
 
-  Constructor get target => targetReference?.asConstructor;
+  Constructor get target => targetReference.asConstructor;
 
   void set target(Constructor target) {
     // Getter vs setter doesn't matter for constructors.
-    targetReference = getMemberReferenceGetter(target);
+    targetReference = getNonNullableMemberReferenceGetter(target);
   }
 
+  @override
   R accept<R>(InitializerVisitor<R> v) => v.visitSuperInitializer(this);
 
-  visitChildren(Visitor v) {
-    target?.acceptReference(v);
-    arguments?.accept(v);
+  @override
+  R accept1<R, A>(InitializerVisitor1<R, A> v, A arg) =>
+      v.visitSuperInitializer(this, arg);
+
+  @override
+  void visitChildren(Visitor v) {
+    target.acceptReference(v);
+    arguments.accept(v);
   }
 
-  transformChildren(Transformer v) {
+  @override
+  void transformChildren(Transformer v) {
+    // ignore: unnecessary_null_comparison
     if (arguments != null) {
-      arguments = arguments.accept<TreeNode>(v);
-      arguments?.parent = this;
+      arguments = v.transform(arguments);
+      arguments.parent = this;
+    }
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    // ignore: unnecessary_null_comparison
+    if (arguments != null) {
+      arguments = v.transform(arguments);
+      arguments.parent = this;
     }
   }
 
@@ -2752,31 +3089,48 @@
   RedirectingInitializer(Constructor target, Arguments arguments)
       : this.byReference(
             // Getter vs setter doesn't matter for constructors.
-            getMemberReferenceGetter(target),
+            getNonNullableMemberReferenceGetter(target),
             arguments);
 
   RedirectingInitializer.byReference(this.targetReference, this.arguments) {
-    arguments?.parent = this;
+    arguments.parent = this;
   }
 
-  Constructor get target => targetReference?.asConstructor;
+  Constructor get target => targetReference.asConstructor;
 
   void set target(Constructor target) {
     // Getter vs setter doesn't matter for constructors.
-    targetReference = getMemberReferenceGetter(target);
+    targetReference = getNonNullableMemberReferenceGetter(target);
   }
 
+  @override
   R accept<R>(InitializerVisitor<R> v) => v.visitRedirectingInitializer(this);
 
-  visitChildren(Visitor v) {
-    target?.acceptReference(v);
-    arguments?.accept(v);
+  @override
+  R accept1<R, A>(InitializerVisitor1<R, A> v, A arg) =>
+      v.visitRedirectingInitializer(this, arg);
+
+  @override
+  void visitChildren(Visitor v) {
+    target.acceptReference(v);
+    arguments.accept(v);
   }
 
-  transformChildren(Transformer v) {
+  @override
+  void transformChildren(Transformer v) {
+    // ignore: unnecessary_null_comparison
     if (arguments != null) {
-      arguments = arguments.accept<TreeNode>(v);
-      arguments?.parent = this;
+      arguments = v.transform(arguments);
+      arguments.parent = this;
+    }
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    // ignore: unnecessary_null_comparison
+    if (arguments != null) {
+      arguments = v.transform(arguments);
+      arguments.parent = this;
     }
   }
 
@@ -2799,19 +3153,36 @@
   VariableDeclaration variable;
 
   LocalInitializer(this.variable) {
-    variable?.parent = this;
+    variable.parent = this;
   }
 
+  @override
   R accept<R>(InitializerVisitor<R> v) => v.visitLocalInitializer(this);
 
-  visitChildren(Visitor v) {
-    variable?.accept(v);
+  @override
+  R accept1<R, A>(InitializerVisitor1<R, A> v, A arg) =>
+      v.visitLocalInitializer(this, arg);
+
+  @override
+  void visitChildren(Visitor v) {
+    variable.accept(v);
   }
 
-  transformChildren(Transformer v) {
+  @override
+  void transformChildren(Transformer v) {
+    // ignore: unnecessary_null_comparison
     if (variable != null) {
-      variable = variable.accept<TreeNode>(v);
-      variable?.parent = this;
+      variable = v.transform(variable);
+      variable.parent = this;
+    }
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    // ignore: unnecessary_null_comparison
+    if (variable != null) {
+      variable = v.transform(variable);
+      variable.parent = this;
     }
   }
 
@@ -2833,14 +3204,27 @@
     statement.parent = this;
   }
 
+  @override
   R accept<R>(InitializerVisitor<R> v) => v.visitAssertInitializer(this);
 
-  visitChildren(Visitor v) {
+  @override
+  R accept1<R, A>(InitializerVisitor1<R, A> v, A arg) =>
+      v.visitAssertInitializer(this, arg);
+
+  @override
+  void visitChildren(Visitor v) {
     statement.accept(v);
   }
 
-  transformChildren(Transformer v) {
-    statement = statement.accept<TreeNode>(v);
+  @override
+  void transformChildren(Transformer v) {
+    statement = v.transform(statement);
+    statement.parent = this;
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    statement = v.transform(statement);
     statement.parent = this;
   }
 
@@ -2890,7 +3274,7 @@
   List<VariableDeclaration> positionalParameters;
   List<VariableDeclaration> namedParameters;
   DartType returnType; // Not null.
-  Statement _body;
+  Statement? _body;
 
   /// The future value type of this is an async function, otherwise `null`.
   ///
@@ -2903,55 +3287,56 @@
   /// here the return types are `Future<Foo>` and `FutureOr<Foo>` for `method1`
   /// and `method2`, respectively, but the future value type is in both cases
   /// `Foo`.
-  DartType futureValueType;
+  DartType? futureValueType;
 
-  void Function() lazyBuilder;
+  void Function()? lazyBuilder;
 
   void _buildLazy() {
-    if (lazyBuilder != null) {
-      void Function() lazyBuilderLocal = lazyBuilder;
+    void Function()? lazyBuilderLocal = lazyBuilder;
+    if (lazyBuilderLocal != null) {
       lazyBuilder = null;
       lazyBuilderLocal();
     }
   }
 
-  Statement get body {
+  Statement? get body {
     _buildLazy();
     return _body;
   }
 
-  void set body(Statement body) {
+  void set body(Statement? body) {
     _buildLazy();
     _body = body;
   }
 
   FunctionNode(this._body,
-      {List<TypeParameter> typeParameters,
-      List<VariableDeclaration> positionalParameters,
-      List<VariableDeclaration> namedParameters,
-      int requiredParameterCount,
+      {List<TypeParameter>? typeParameters,
+      List<VariableDeclaration>? positionalParameters,
+      List<VariableDeclaration>? namedParameters,
+      int? requiredParameterCount,
       this.returnType: const DynamicType(),
       this.asyncMarker: AsyncMarker.Sync,
-      this.dartAsyncMarker,
+      AsyncMarker? dartAsyncMarker,
       this.futureValueType})
       : this.positionalParameters =
             positionalParameters ?? <VariableDeclaration>[],
         this.requiredParameterCount =
             requiredParameterCount ?? positionalParameters?.length ?? 0,
         this.namedParameters = namedParameters ?? <VariableDeclaration>[],
-        this.typeParameters = typeParameters ?? <TypeParameter>[] {
+        this.typeParameters = typeParameters ?? <TypeParameter>[],
+        this.dartAsyncMarker = dartAsyncMarker ?? asyncMarker {
+    // ignore: unnecessary_null_comparison
     assert(returnType != null);
     setParents(this.typeParameters, this);
     setParents(this.positionalParameters, this);
     setParents(this.namedParameters, this);
     _body?.parent = this;
-    dartAsyncMarker ??= asyncMarker;
   }
 
   static DartType _getTypeOfVariable(VariableDeclaration node) => node.type;
 
   static NamedType _getNamedTypeOfVariable(VariableDeclaration node) {
-    return new NamedType(node.name, node.type, isRequired: node.isRequired);
+    return new NamedType(node.name!, node.type, isRequired: node.isRequired);
   }
 
   /// Returns the function type of the node reusing its type parameters.
@@ -2963,7 +3348,7 @@
   /// type of the enclosing generic function and in combination with
   /// [FunctionType.withoutTypeParameters].
   FunctionType computeThisFunctionType(Nullability nullability) {
-    TreeNode parent = this.parent;
+    TreeNode? parent = this.parent;
     List<NamedType> named =
         namedParameters.map(_getNamedTypeOfVariable).toList(growable: false);
     named.sort();
@@ -2971,7 +3356,7 @@
     // transformations like erasure don't work.
     List<TypeParameter> typeParametersCopy = new List<TypeParameter>.from(
         parent is Constructor
-            ? parent.enclosingClass.typeParameters
+            ? parent.enclosingClass!.typeParameters
             : typeParameters);
     return new FunctionType(
         positionalParameters.map(_getTypeOfVariable).toList(growable: false),
@@ -3006,13 +3391,12 @@
   /// constructor invocations.
   FunctionType computeAliasedConstructorFunctionType(
       Typedef typedef, Library library) {
-    TreeNode parent = this.parent;
     assert(parent is Constructor, "Only run this method on constructors");
-    Constructor parentConstructor = parent;
+    Constructor parentConstructor = parent as Constructor;
     // We need create a copy of the list of type parameters, otherwise
     // transformations like erasure don't work.
     List<TypeParameter> classTypeParametersCopy =
-        List.from(parentConstructor.enclosingClass.typeParameters);
+        List.from(parentConstructor.enclosingClass!.typeParameters);
     List<TypeParameter> typedefTypeParametersCopy =
         List.from(typedef.typeParameters);
     List<DartType> asTypeArguments =
@@ -3022,7 +3406,7 @@
     DartType unaliasedTypedef = typedefType.unalias;
     assert(unaliasedTypedef is InterfaceType,
         "[typedef] is assumed to resolve to an interface type");
-    InterfaceType targetType = unaliasedTypedef;
+    InterfaceType targetType = unaliasedTypedef as InterfaceType;
     Substitution substitution = Substitution.fromPairs(
         classTypeParametersCopy, targetType.typeArguments);
     List<DartType> positional = positionalParameters
@@ -3031,7 +3415,7 @@
         .toList(growable: false);
     List<NamedType> named = namedParameters
         .map((VariableDeclaration decl) => NamedType(
-            decl.name, substitution.substituteType(decl.type),
+            decl.name!, substitution.substituteType(decl.type),
             isRequired: decl.isRequired))
         .toList(growable: false);
     named.sort();
@@ -3066,7 +3450,7 @@
     DartType unaliasedTypedef = typedefType.unalias;
     assert(unaliasedTypedef is InterfaceType,
         "[typedef] is assumed to resolve to an interface type");
-    InterfaceType targetType = unaliasedTypedef;
+    InterfaceType targetType = unaliasedTypedef as InterfaceType;
     Substitution substitution = Substitution.fromPairs(
         classTypeParametersCopy, targetType.typeArguments);
     List<DartType> positional = positionalParameters
@@ -3075,7 +3459,7 @@
         .toList(growable: false);
     List<NamedType> named = namedParameters
         .map((VariableDeclaration decl) => NamedType(
-            decl.name, substitution.substituteType(decl.type),
+            decl.name!, substitution.substituteType(decl.type),
             isRequired: decl.isRequired))
         .toList(growable: false);
     named.sort();
@@ -3085,23 +3469,42 @@
         requiredParameterCount: requiredParameterCount);
   }
 
+  @override
   R accept<R>(TreeVisitor<R> v) => v.visitFunctionNode(this);
 
-  visitChildren(Visitor v) {
+  @override
+  R accept1<R, A>(TreeVisitor1<R, A> v, A arg) =>
+      v.visitFunctionNode(this, arg);
+
+  @override
+  void visitChildren(Visitor v) {
     visitList(typeParameters, v);
     visitList(positionalParameters, v);
     visitList(namedParameters, v);
-    returnType?.accept(v);
+    returnType.accept(v);
     body?.accept(v);
   }
 
-  transformChildren(Transformer v) {
-    transformList(typeParameters, v, this);
-    transformList(positionalParameters, v, this);
-    transformList(namedParameters, v, this);
+  @override
+  void transformChildren(Transformer v) {
+    v.transformList(typeParameters, this);
+    v.transformList(positionalParameters, this);
+    v.transformList(namedParameters, this);
     returnType = v.visitDartType(returnType);
     if (body != null) {
-      body = body.accept<TreeNode>(v);
+      body = v.transform(body!);
+      body?.parent = this;
+    }
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    v.transformTypeParameterList(typeParameters, this);
+    v.transformVariableDeclarationList(positionalParameters, this);
+    v.transformVariableDeclarationList(namedParameters, this);
+    returnType = v.visitDartType(returnType, cannotRemoveSentinel);
+    if (body != null) {
+      body = v.transformOrRemoveStatement(body!);
       body?.parent = this;
     }
   }
@@ -3206,8 +3609,7 @@
     DartType type = getStaticType(context);
     while (type is TypeParameterType) {
       TypeParameterType typeParameterType = type;
-      type =
-          typeParameterType.promotedBound ?? typeParameterType.parameter.bound;
+      type = typeParameterType.bound;
     }
     if (type is NullType) {
       return context.typeEnvironment.coreTypes
@@ -3217,7 +3619,7 @@
           .bottomInterfaceType(superclass, type.nullability);
     }
     if (type is InterfaceType) {
-      List<DartType> upcastTypeArguments = context.typeEnvironment
+      List<DartType>? upcastTypeArguments = context.typeEnvironment
           .getTypeArgumentsAsInstanceOf(type, superclass);
       if (upcastTypeArguments != null) {
         return new InterfaceType(
@@ -3257,18 +3659,20 @@
         .rawType(superclass, context.nonNullable);
   }
 
+  @override
   R accept<R>(ExpressionVisitor<R> v);
+
+  @override
   R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg);
 
   int get precedence => astToText.Precedence.of(this);
 
+  @override
   String toText(AstTextStrategy strategy) {
     AstPrinter printer = new AstPrinter(strategy);
     printer.writeExpression(this);
     return printer.getText();
   }
-
-  void toTextInternal(AstPrinter printer);
 }
 
 /// An expression containing compile-time errors.
@@ -3304,6 +3708,9 @@
   void transformChildren(Transformer v) {}
 
   @override
+  void transformOrRemoveChildren(RemovingTransformer v) {}
+
+  @override
   String toString() {
     return "InvalidExpression(${toStringInternal()})";
   }
@@ -3319,10 +3726,13 @@
 /// Read a local variable, a local function, or a function parameter.
 class VariableGet extends Expression {
   VariableDeclaration variable;
-  DartType promotedType; // Null if not promoted.
+  DartType? promotedType; // Null if not promoted.
 
-  VariableGet(this.variable, [this.promotedType]) : assert(variable != null);
+  VariableGet(this.variable, [this.promotedType])
+      // ignore: unnecessary_null_comparison
+      : assert(variable != null);
 
+  @override
   DartType getStaticType(StaticTypeContext context) =>
       getStaticTypeInternal(context);
 
@@ -3331,17 +3741,34 @@
     return promotedType ?? variable.type;
   }
 
+  @override
   R accept<R>(ExpressionVisitor<R> v) => v.visitVariableGet(this);
+
+  @override
   R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
       v.visitVariableGet(this, arg);
 
-  visitChildren(Visitor v) {
+  @override
+  void visitChildren(Visitor v) {
     promotedType?.accept(v);
   }
 
-  transformChildren(Transformer v) {
+  @override
+  void transformChildren(Transformer v) {
     if (promotedType != null) {
-      promotedType = v.visitDartType(promotedType);
+      promotedType = v.visitDartType(promotedType!);
+    }
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    if (promotedType != null) {
+      DartType newPromotedType = v.visitDartType(promotedType!, dummyDartType);
+      if (identical(newPromotedType, dummyDartType)) {
+        promotedType = null;
+      } else {
+        promotedType = newPromotedType;
+      }
     }
   }
 
@@ -3355,7 +3782,7 @@
     printer.write(printer.getVariableName(variable));
     if (promotedType != null) {
       printer.write('{');
-      printer.writeType(promotedType);
+      printer.writeType(promotedType!);
       printer.write('}');
     }
   }
@@ -3368,10 +3795,13 @@
   VariableDeclaration variable;
   Expression value;
 
-  VariableSet(this.variable, this.value) : assert(variable != null) {
-    value?.parent = this;
+  VariableSet(this.variable, this.value)
+      // ignore: unnecessary_null_comparison
+      : assert(variable != null) {
+    value.parent = this;
   }
 
+  @override
   DartType getStaticType(StaticTypeContext context) =>
       getStaticTypeInternal(context);
 
@@ -3379,18 +3809,33 @@
   DartType getStaticTypeInternal(StaticTypeContext context) =>
       value.getStaticType(context);
 
+  @override
   R accept<R>(ExpressionVisitor<R> v) => v.visitVariableSet(this);
+
+  @override
   R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
       v.visitVariableSet(this, arg);
 
-  visitChildren(Visitor v) {
-    value?.accept(v);
+  @override
+  void visitChildren(Visitor v) {
+    value.accept(v);
   }
 
-  transformChildren(Transformer v) {
+  @override
+  void transformChildren(Transformer v) {
+    // ignore: unnecessary_null_comparison
     if (value != null) {
-      value = value.accept<TreeNode>(v);
-      value?.parent = this;
+      value = v.transform(value);
+      value.parent = this;
+    }
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    // ignore: unnecessary_null_comparison
+    if (value != null) {
+      value = v.transform(value);
+      value.parent = this;
     }
   }
 
@@ -3440,10 +3885,13 @@
   Name name;
 
   DynamicGet(this.kind, this.receiver, this.name) {
-    receiver?.parent = this;
+    receiver.parent = this;
   }
 
+  @override
   R accept<R>(ExpressionVisitor<R> v) => v.visitDynamicGet(this);
+
+  @override
   R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
       v.visitDynamicGet(this, arg);
 
@@ -3458,20 +3906,29 @@
       case DynamicAccessKind.Unresolved:
         return const InvalidType();
     }
-    return const DynamicType();
   }
 
   @override
   void visitChildren(Visitor v) {
-    receiver?.accept(v);
-    name?.accept(v);
+    receiver.accept(v);
+    name.accept(v);
   }
 
   @override
   void transformChildren(Transformer v) {
+    // ignore: unnecessary_null_comparison
     if (receiver != null) {
-      receiver = receiver.accept<TreeNode>(v);
-      receiver?.parent = this;
+      receiver = v.transform(receiver);
+      receiver.parent = this;
+    }
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    // ignore: unnecessary_null_comparison
+    if (receiver != null) {
+      receiver = v.transform(receiver);
+      receiver.parent = this;
     }
   }
 
@@ -3514,41 +3971,59 @@
   Reference interfaceTargetReference;
 
   InstanceGet(InstanceAccessKind kind, Expression receiver, Name name,
-      {Member interfaceTarget, DartType resultType})
+      {required Member interfaceTarget, required DartType resultType})
       : this.byReference(kind, receiver, name,
-            interfaceTargetReference: getMemberReferenceGetter(interfaceTarget),
+            interfaceTargetReference:
+                getNonNullableMemberReferenceGetter(interfaceTarget),
             resultType: resultType);
 
   InstanceGet.byReference(this.kind, this.receiver, this.name,
-      {this.interfaceTargetReference, this.resultType})
+      {required this.interfaceTargetReference, required this.resultType})
+      // ignore: unnecessary_null_comparison
       : assert(interfaceTargetReference != null),
+        // ignore: unnecessary_null_comparison
         assert(resultType != null) {
-    receiver?.parent = this;
+    receiver.parent = this;
   }
 
-  Member get interfaceTarget => interfaceTargetReference?.asMember;
+  Member get interfaceTarget => interfaceTargetReference.asMember;
 
   void set interfaceTarget(Member member) {
-    interfaceTargetReference = getMemberReferenceSetter(member);
+    interfaceTargetReference = getNonNullableMemberReferenceSetter(member);
   }
 
   @override
   DartType getStaticTypeInternal(StaticTypeContext context) => resultType;
 
+  @override
   R accept<R>(ExpressionVisitor<R> v) => v.visitInstanceGet(this);
+
+  @override
   R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
       v.visitInstanceGet(this, arg);
 
-  visitChildren(Visitor v) {
-    receiver?.accept(v);
-    interfaceTarget?.acceptReference(v);
-    name?.accept(v);
+  @override
+  void visitChildren(Visitor v) {
+    receiver.accept(v);
+    interfaceTarget.acceptReference(v);
+    name.accept(v);
   }
 
-  transformChildren(Transformer v) {
+  @override
+  void transformChildren(Transformer v) {
+    // ignore: unnecessary_null_comparison
     if (receiver != null) {
-      receiver = receiver.accept<TreeNode>(v);
-      receiver?.parent = this;
+      receiver = v.transform(receiver);
+      receiver.parent = this;
+    }
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    // ignore: unnecessary_null_comparison
+    if (receiver != null) {
+      receiver = v.transform(receiver);
+      receiver.parent = this;
     }
   }
 
@@ -3572,24 +4047,40 @@
   Expression receiver;
 
   FunctionTearOff(this.receiver) {
-    receiver?.parent = this;
+    receiver.parent = this;
   }
 
+  @override
   DartType getStaticTypeInternal(StaticTypeContext context) =>
       receiver.getStaticType(context);
 
+  @override
   R accept<R>(ExpressionVisitor<R> v) => v.visitFunctionTearOff(this);
+
+  @override
   R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
       v.visitFunctionTearOff(this, arg);
 
-  visitChildren(Visitor v) {
-    receiver?.accept(v);
+  @override
+  void visitChildren(Visitor v) {
+    receiver.accept(v);
   }
 
-  transformChildren(Transformer v) {
+  @override
+  void transformChildren(Transformer v) {
+    // ignore: unnecessary_null_comparison
     if (receiver != null) {
-      receiver = receiver.accept<TreeNode>(v);
-      receiver?.parent = this;
+      receiver = v.transform(receiver);
+      receiver.parent = this;
+    }
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    // ignore: unnecessary_null_comparison
+    if (receiver != null) {
+      receiver = v.transform(receiver);
+      receiver.parent = this;
     }
   }
 
@@ -3631,39 +4122,55 @@
   Reference interfaceTargetReference;
 
   InstanceTearOff(InstanceAccessKind kind, Expression receiver, Name name,
-      {Procedure interfaceTarget, DartType resultType})
+      {required Procedure interfaceTarget, required DartType resultType})
       : this.byReference(kind, receiver, name,
-            interfaceTargetReference: getMemberReferenceGetter(interfaceTarget),
+            interfaceTargetReference:
+                getNonNullableMemberReferenceGetter(interfaceTarget),
             resultType: resultType);
 
   InstanceTearOff.byReference(this.kind, this.receiver, this.name,
-      {this.interfaceTargetReference, this.resultType}) {
-    receiver?.parent = this;
+      {required this.interfaceTargetReference, required this.resultType}) {
+    receiver.parent = this;
   }
 
-  Procedure get interfaceTarget => interfaceTargetReference?.asMember;
+  Procedure get interfaceTarget => interfaceTargetReference.asProcedure;
 
-  void set interfaceTarget(Member member) {
-    interfaceTargetReference = getMemberReferenceSetter(member);
+  void set interfaceTarget(Procedure procedure) {
+    interfaceTargetReference = getNonNullableMemberReferenceSetter(procedure);
   }
 
   @override
   DartType getStaticTypeInternal(StaticTypeContext context) => resultType;
 
+  @override
   R accept<R>(ExpressionVisitor<R> v) => v.visitInstanceTearOff(this);
+
+  @override
   R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
       v.visitInstanceTearOff(this, arg);
 
-  visitChildren(Visitor v) {
-    receiver?.accept(v);
-    interfaceTarget?.acceptReference(v);
-    name?.accept(v);
+  @override
+  void visitChildren(Visitor v) {
+    receiver.accept(v);
+    interfaceTarget.acceptReference(v);
+    name.accept(v);
   }
 
-  transformChildren(Transformer v) {
+  @override
+  void transformChildren(Transformer v) {
+    // ignore: unnecessary_null_comparison
     if (receiver != null) {
-      receiver = receiver.accept<TreeNode>(v);
-      receiver?.parent = this;
+      receiver = v.transform(receiver);
+      receiver.parent = this;
+    }
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    // ignore: unnecessary_null_comparison
+    if (receiver != null) {
+      receiver = v.transform(receiver);
+      receiver.parent = this;
     }
   }
 
@@ -3688,27 +4195,28 @@
   Expression receiver;
   Name name;
 
-  Reference interfaceTargetReference;
+  Reference? interfaceTargetReference;
 
-  PropertyGet(Expression receiver, Name name, [Member interfaceTarget])
+  PropertyGet(Expression receiver, Name name, [Member? interfaceTarget])
       : this.byReference(
             receiver, name, getMemberReferenceGetter(interfaceTarget));
 
   PropertyGet.byReference(
       this.receiver, this.name, this.interfaceTargetReference) {
-    receiver?.parent = this;
+    receiver.parent = this;
   }
 
-  Member get interfaceTarget => interfaceTargetReference?.asMember;
+  Member? get interfaceTarget => interfaceTargetReference?.asMember;
 
-  void set interfaceTarget(Member member) {
+  void set interfaceTarget(Member? member) {
     interfaceTargetReference = getMemberReferenceGetter(member);
   }
 
+  @override
   DartType getStaticTypeInternal(StaticTypeContext context) {
-    Member interfaceTarget = this.interfaceTarget;
+    Member? interfaceTarget = this.interfaceTarget;
     if (interfaceTarget != null) {
-      Class superclass = interfaceTarget.enclosingClass;
+      Class superclass = interfaceTarget.enclosingClass!;
       InterfaceType receiverType =
           receiver.getStaticTypeAsInstanceOf(superclass, context);
       return Substitution.fromInterfaceType(receiverType)
@@ -3724,20 +4232,35 @@
     return const DynamicType();
   }
 
+  @override
   R accept<R>(ExpressionVisitor<R> v) => v.visitPropertyGet(this);
+
+  @override
   R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
       v.visitPropertyGet(this, arg);
 
-  visitChildren(Visitor v) {
-    receiver?.accept(v);
+  @override
+  void visitChildren(Visitor v) {
+    receiver.accept(v);
     interfaceTarget?.acceptReference(v);
-    name?.accept(v);
+    name.accept(v);
   }
 
-  transformChildren(Transformer v) {
+  @override
+  void transformChildren(Transformer v) {
+    // ignore: unnecessary_null_comparison
     if (receiver != null) {
-      receiver = receiver.accept<TreeNode>(v);
-      receiver?.parent = this;
+      receiver = v.transform(receiver);
+      receiver.parent = this;
+    }
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    // ignore: unnecessary_null_comparison
+    if (receiver != null) {
+      receiver = v.transform(receiver);
+      receiver.parent = this;
     }
   }
 
@@ -3762,31 +4285,53 @@
   Expression value;
 
   DynamicSet(this.kind, this.receiver, this.name, this.value) {
-    receiver?.parent = this;
-    value?.parent = this;
+    receiver.parent = this;
+    value.parent = this;
   }
 
+  @override
   DartType getStaticTypeInternal(StaticTypeContext context) =>
       value.getStaticType(context);
 
+  @override
   R accept<R>(ExpressionVisitor<R> v) => v.visitDynamicSet(this);
+
+  @override
   R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
       v.visitDynamicSet(this, arg);
 
-  visitChildren(Visitor v) {
-    receiver?.accept(v);
-    name?.accept(v);
-    value?.accept(v);
+  @override
+  void visitChildren(Visitor v) {
+    receiver.accept(v);
+    name.accept(v);
+    value.accept(v);
   }
 
-  transformChildren(Transformer v) {
+  @override
+  void transformChildren(Transformer v) {
+    // ignore: unnecessary_null_comparison
     if (receiver != null) {
-      receiver = receiver.accept<TreeNode>(v);
-      receiver?.parent = this;
+      receiver = v.transform(receiver);
+      receiver.parent = this;
     }
+    // ignore: unnecessary_null_comparison
     if (value != null) {
-      value = value.accept<TreeNode>(v);
-      value?.parent = this;
+      value = v.transform(value);
+      value.parent = this;
+    }
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    // ignore: unnecessary_null_comparison
+    if (receiver != null) {
+      receiver = v.transform(receiver);
+      receiver.parent = this;
+    }
+    // ignore: unnecessary_null_comparison
+    if (value != null) {
+      value = v.transform(value);
+      value.parent = this;
     }
   }
 
@@ -3818,46 +4363,69 @@
 
   InstanceSet(
       InstanceAccessKind kind, Expression receiver, Name name, Expression value,
-      {Member interfaceTarget})
+      {required Member interfaceTarget})
       : this.byReference(kind, receiver, name, value,
             interfaceTargetReference:
-                getMemberReferenceSetter(interfaceTarget));
+                getNonNullableMemberReferenceSetter(interfaceTarget));
 
   InstanceSet.byReference(this.kind, this.receiver, this.name, this.value,
-      {this.interfaceTargetReference})
+      {required this.interfaceTargetReference})
+      // ignore: unnecessary_null_comparison
       : assert(interfaceTargetReference != null) {
-    receiver?.parent = this;
-    value?.parent = this;
+    receiver.parent = this;
+    value.parent = this;
   }
 
-  Member get interfaceTarget => interfaceTargetReference?.asMember;
+  Member get interfaceTarget => interfaceTargetReference.asMember;
 
   void set interfaceTarget(Member member) {
-    interfaceTargetReference = getMemberReferenceSetter(member);
+    interfaceTargetReference = getNonNullableMemberReferenceSetter(member);
   }
 
+  @override
   DartType getStaticTypeInternal(StaticTypeContext context) =>
       value.getStaticType(context);
 
+  @override
   R accept<R>(ExpressionVisitor<R> v) => v.visitInstanceSet(this);
+
+  @override
   R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
       v.visitInstanceSet(this, arg);
 
-  visitChildren(Visitor v) {
-    receiver?.accept(v);
-    interfaceTarget?.acceptReference(v);
-    name?.accept(v);
-    value?.accept(v);
+  @override
+  void visitChildren(Visitor v) {
+    receiver.accept(v);
+    interfaceTarget.acceptReference(v);
+    name.accept(v);
+    value.accept(v);
   }
 
-  transformChildren(Transformer v) {
+  @override
+  void transformChildren(Transformer v) {
+    // ignore: unnecessary_null_comparison
     if (receiver != null) {
-      receiver = receiver.accept<TreeNode>(v);
-      receiver?.parent = this;
+      receiver = v.transform(receiver);
+      receiver.parent = this;
     }
+    // ignore: unnecessary_null_comparison
     if (value != null) {
-      value = value.accept<TreeNode>(v);
-      value?.parent = this;
+      value = v.transform(value);
+      value.parent = this;
+    }
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    // ignore: unnecessary_null_comparison
+    if (receiver != null) {
+      receiver = v.transform(receiver);
+      receiver.parent = this;
+    }
+    // ignore: unnecessary_null_comparison
+    if (value != null) {
+      value = v.transform(value);
+      value.parent = this;
     }
   }
 
@@ -3887,47 +4455,69 @@
   Name name;
   Expression value;
 
-  Reference interfaceTargetReference;
+  Reference? interfaceTargetReference;
 
   PropertySet(Expression receiver, Name name, Expression value,
-      [Member interfaceTarget])
+      [Member? interfaceTarget])
       : this.byReference(
             receiver, name, value, getMemberReferenceSetter(interfaceTarget));
 
   PropertySet.byReference(
       this.receiver, this.name, this.value, this.interfaceTargetReference) {
-    receiver?.parent = this;
-    value?.parent = this;
+    receiver.parent = this;
+    value.parent = this;
   }
 
-  Member get interfaceTarget => interfaceTargetReference?.asMember;
+  Member? get interfaceTarget => interfaceTargetReference?.asMember;
 
-  void set interfaceTarget(Member member) {
+  void set interfaceTarget(Member? member) {
     interfaceTargetReference = getMemberReferenceSetter(member);
   }
 
+  @override
   DartType getStaticTypeInternal(StaticTypeContext context) =>
       value.getStaticType(context);
 
+  @override
   R accept<R>(ExpressionVisitor<R> v) => v.visitPropertySet(this);
+
+  @override
   R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
       v.visitPropertySet(this, arg);
 
-  visitChildren(Visitor v) {
-    receiver?.accept(v);
+  @override
+  void visitChildren(Visitor v) {
+    receiver.accept(v);
     interfaceTarget?.acceptReference(v);
-    name?.accept(v);
-    value?.accept(v);
+    name.accept(v);
+    value.accept(v);
   }
 
-  transformChildren(Transformer v) {
+  @override
+  void transformChildren(Transformer v) {
+    // ignore: unnecessary_null_comparison
     if (receiver != null) {
-      receiver = receiver.accept<TreeNode>(v);
-      receiver?.parent = this;
+      receiver = v.transform(receiver);
+      receiver.parent = this;
     }
+    // ignore: unnecessary_null_comparison
     if (value != null) {
-      value = value.accept<TreeNode>(v);
-      value?.parent = this;
+      value = v.transform(value);
+      value.parent = this;
+    }
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    // ignore: unnecessary_null_comparison
+    if (receiver != null) {
+      receiver = v.transform(receiver);
+      receiver.parent = this;
+    }
+    // ignore: unnecessary_null_comparison
+    if (value != null) {
+      value = v.transform(value);
+      value.parent = this;
     }
   }
 
@@ -3953,46 +4543,56 @@
 class SuperPropertyGet extends Expression {
   Name name;
 
-  Reference interfaceTargetReference;
+  Reference? interfaceTargetReference;
 
-  SuperPropertyGet(Name name, [Member interfaceTarget])
+  SuperPropertyGet(Name name, [Member? interfaceTarget])
       : this.byReference(name, getMemberReferenceGetter(interfaceTarget));
 
   SuperPropertyGet.byReference(this.name, this.interfaceTargetReference);
 
-  Member get interfaceTarget => interfaceTargetReference?.asMember;
+  Member? get interfaceTarget => interfaceTargetReference?.asMember;
 
-  void set interfaceTarget(Member member) {
+  void set interfaceTarget(Member? member) {
     interfaceTargetReference = getMemberReferenceGetter(member);
   }
 
+  @override
   DartType getStaticTypeInternal(StaticTypeContext context) {
+    Member? interfaceTarget = this.interfaceTarget;
     if (interfaceTarget == null) {
       // TODO(johnniwinther): SuperPropertyGet without a target should be
       // replaced by invalid expressions.
       return const DynamicType();
     }
-    Class declaringClass = interfaceTarget.enclosingClass;
+    Class declaringClass = interfaceTarget.enclosingClass!;
     if (declaringClass.typeParameters.isEmpty) {
       return interfaceTarget.getterType;
     }
-    List<DartType> receiverArguments = context.typeEnvironment
-        .getTypeArgumentsAsInstanceOf(context.thisType, declaringClass);
+    List<DartType>? receiverArguments = context.typeEnvironment
+        .getTypeArgumentsAsInstanceOf(context.thisType!, declaringClass);
     return Substitution.fromPairs(
-            declaringClass.typeParameters, receiverArguments)
+            declaringClass.typeParameters, receiverArguments!)
         .substituteType(interfaceTarget.getterType);
   }
 
+  @override
   R accept<R>(ExpressionVisitor<R> v) => v.visitSuperPropertyGet(this);
+
+  @override
   R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
       v.visitSuperPropertyGet(this, arg);
 
-  visitChildren(Visitor v) {
+  @override
+  void visitChildren(Visitor v) {
     interfaceTarget?.acceptReference(v);
-    name?.accept(v);
+    name.accept(v);
   }
 
-  transformChildren(Transformer v) {}
+  @override
+  void transformChildren(Transformer v) {}
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {}
 
   @override
   String toString() {
@@ -4015,7 +4615,7 @@
   Name name;
   Expression value;
 
-  Reference interfaceTargetReference;
+  Reference? interfaceTargetReference;
 
   SuperPropertySet(Name name, Expression value, Member interfaceTarget)
       : this.byReference(
@@ -4023,32 +4623,48 @@
 
   SuperPropertySet.byReference(
       this.name, this.value, this.interfaceTargetReference) {
-    value?.parent = this;
+    value.parent = this;
   }
 
-  Member get interfaceTarget => interfaceTargetReference?.asMember;
+  Member? get interfaceTarget => interfaceTargetReference?.asMember;
 
-  void set interfaceTarget(Member member) {
+  void set interfaceTarget(Member? member) {
     interfaceTargetReference = getMemberReferenceSetter(member);
   }
 
+  @override
   DartType getStaticTypeInternal(StaticTypeContext context) =>
       value.getStaticType(context);
 
+  @override
   R accept<R>(ExpressionVisitor<R> v) => v.visitSuperPropertySet(this);
+
+  @override
   R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
       v.visitSuperPropertySet(this, arg);
 
-  visitChildren(Visitor v) {
+  @override
+  void visitChildren(Visitor v) {
     interfaceTarget?.acceptReference(v);
-    name?.accept(v);
-    value?.accept(v);
+    name.accept(v);
+    value.accept(v);
   }
 
-  transformChildren(Transformer v) {
+  @override
+  void transformChildren(Transformer v) {
+    // ignore: unnecessary_null_comparison
     if (value != null) {
-      value = value.accept<TreeNode>(v);
-      value?.parent = this;
+      value = v.transform(value);
+      value.parent = this;
+    }
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    // ignore: unnecessary_null_comparison
+    if (value != null) {
+      value = v.transform(value);
+      value.parent = this;
     }
   }
 
@@ -4071,28 +4687,38 @@
   /// A static field, getter, or method (for tear-off).
   Reference targetReference;
 
-  StaticGet(Member target) : this.byReference(getMemberReferenceGetter(target));
+  StaticGet(Member target)
+      : this.byReference(getNonNullableMemberReferenceGetter(target));
 
   StaticGet.byReference(this.targetReference);
 
-  Member get target => targetReference?.asMember;
+  Member get target => targetReference.asMember;
 
   void set target(Member target) {
-    targetReference = getMemberReferenceGetter(target);
+    targetReference = getNonNullableMemberReferenceGetter(target);
   }
 
+  @override
   DartType getStaticTypeInternal(StaticTypeContext context) =>
       target.getterType;
 
+  @override
   R accept<R>(ExpressionVisitor<R> v) => v.visitStaticGet(this);
+
+  @override
   R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
       v.visitStaticGet(this, arg);
 
-  visitChildren(Visitor v) {
-    target?.acceptReference(v);
+  @override
+  void visitChildren(Visitor v) {
+    target.acceptReference(v);
   }
 
-  transformChildren(Transformer v) {}
+  @override
+  void transformChildren(Transformer v) {}
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {}
 
   @override
   String toString() {
@@ -4110,28 +4736,37 @@
   Reference targetReference;
 
   StaticTearOff(Procedure target)
-      : this.byReference(getMemberReferenceGetter(target));
+      : this.byReference(getNonNullableMemberReferenceGetter(target));
 
   StaticTearOff.byReference(this.targetReference);
 
-  Procedure get target => targetReference?.asProcedure;
+  Procedure get target => targetReference.asProcedure;
 
   void set target(Procedure target) {
-    targetReference = getMemberReferenceGetter(target);
+    targetReference = getNonNullableMemberReferenceGetter(target);
   }
 
+  @override
   DartType getStaticTypeInternal(StaticTypeContext context) =>
       target.getterType;
 
+  @override
   R accept<R>(ExpressionVisitor<R> v) => v.visitStaticTearOff(this);
+
+  @override
   R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
       v.visitStaticTearOff(this, arg);
 
-  visitChildren(Visitor v) {
-    target?.acceptReference(v);
+  @override
+  void visitChildren(Visitor v) {
+    target.acceptReference(v);
   }
 
-  transformChildren(Transformer v) {}
+  @override
+  void transformChildren(Transformer v) {}
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {}
 
   @override
   String toString() {
@@ -4153,34 +4788,50 @@
   Expression value;
 
   StaticSet(Member target, Expression value)
-      : this.byReference(getMemberReferenceSetter(target), value);
+      : this.byReference(getNonNullableMemberReferenceSetter(target), value);
 
   StaticSet.byReference(this.targetReference, this.value) {
-    value?.parent = this;
+    value.parent = this;
   }
 
-  Member get target => targetReference?.asMember;
+  Member get target => targetReference.asMember;
 
   void set target(Member target) {
-    targetReference = getMemberReferenceSetter(target);
+    targetReference = getNonNullableMemberReferenceSetter(target);
   }
 
+  @override
   DartType getStaticTypeInternal(StaticTypeContext context) =>
       value.getStaticType(context);
 
+  @override
   R accept<R>(ExpressionVisitor<R> v) => v.visitStaticSet(this);
+
+  @override
   R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
       v.visitStaticSet(this, arg);
 
-  visitChildren(Visitor v) {
-    target?.acceptReference(v);
-    value?.accept(v);
+  @override
+  void visitChildren(Visitor v) {
+    target.acceptReference(v);
+    value.accept(v);
   }
 
-  transformChildren(Transformer v) {
+  @override
+  void transformChildren(Transformer v) {
+    // ignore: unnecessary_null_comparison
     if (value != null) {
-      value = value.accept<TreeNode>(v);
-      value?.parent = this;
+      value = v.transform(value);
+      value.parent = this;
+    }
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    // ignore: unnecessary_null_comparison
+    if (value != null) {
+      value = v.transform(value);
+      value.parent = this;
     }
   }
 
@@ -4205,7 +4856,7 @@
   List<NamedExpression> named;
 
   Arguments(this.positional,
-      {List<DartType> types, List<NamedExpression> named})
+      {List<DartType>? types, List<NamedExpression>? named})
       : this.types = types ?? <DartType>[],
         this.named = named ?? <NamedExpression>[] {
     setParents(this.positional, this);
@@ -4223,7 +4874,7 @@
             .map<Expression>((p) => new VariableGet(p))
             .toList(),
         named: function.namedParameters
-            .map((p) => new NamedExpression(p.name, new VariableGet(p)))
+            .map((p) => new NamedExpression(p.name!, new VariableGet(p)))
             .toList(),
         types: function.typeParameters
             .map<DartType>((p) =>
@@ -4232,18 +4883,31 @@
             .toList());
   }
 
+  @override
   R accept<R>(TreeVisitor<R> v) => v.visitArguments(this);
 
-  visitChildren(Visitor v) {
+  @override
+  R accept1<R, A>(TreeVisitor1<R, A> v, A arg) => v.visitArguments(this, arg);
+
+  @override
+  void visitChildren(Visitor v) {
     visitList(types, v);
     visitList(positional, v);
     visitList(named, v);
   }
 
-  transformChildren(Transformer v) {
-    transformTypeList(types, v);
-    transformList(positional, v, this);
-    transformList(named, v, this);
+  @override
+  void transformChildren(Transformer v) {
+    v.transformDartTypeList(types);
+    v.transformList(positional, this);
+    v.transformList(named, this);
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    v.transformDartTypeList(types);
+    v.transformExpressionList(positional, this);
+    v.transformNamedExpressionList(named, this);
   }
 
   @override
@@ -4251,12 +4915,14 @@
     return "Arguments(${toStringInternal()})";
   }
 
+  @override
   String toText(AstTextStrategy strategy) {
     AstPrinter printer = new AstPrinter(strategy);
     printer.writeArguments(this);
     return printer.getText();
   }
 
+  @override
   void toTextInternal(AstPrinter printer, {bool includeTypeArguments: true}) {
     if (includeTypeArguments) {
       printer.writeTypeArguments(types);
@@ -4289,19 +4955,36 @@
   Expression value;
 
   NamedExpression(this.name, this.value) {
-    value?.parent = this;
+    value.parent = this;
   }
 
+  @override
   R accept<R>(TreeVisitor<R> v) => v.visitNamedExpression(this);
 
-  visitChildren(Visitor v) {
-    value?.accept(v);
+  @override
+  R accept1<R, A>(TreeVisitor1<R, A> v, A arg) =>
+      v.visitNamedExpression(this, arg);
+
+  @override
+  void visitChildren(Visitor v) {
+    value.accept(v);
   }
 
-  transformChildren(Transformer v) {
+  @override
+  void transformChildren(Transformer v) {
+    // ignore: unnecessary_null_comparison
     if (value != null) {
-      value = value.accept<TreeNode>(v);
-      value?.parent = this;
+      value = v.transform(value);
+      value.parent = this;
+    }
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    // ignore: unnecessary_null_comparison
+    if (value != null) {
+      value = v.transform(value);
+      value.parent = this;
     }
   }
 
@@ -4310,12 +4993,14 @@
     return "NamedExpression(${toStringInternal()})";
   }
 
+  @override
   String toText(AstTextStrategy strategy) {
     AstPrinter printer = new AstPrinter(strategy);
     toTextInternal(printer);
     return printer.getText();
   }
 
+  @override
   void toTextInternal(AstPrinter printer) {
     printer.write(name);
     printer.write(': ');
@@ -4330,20 +5015,22 @@
   void set arguments(Arguments value);
 
   /// Name of the invoked method.
-  ///
-  /// May be `null` if the target is a synthetic static member without a name.
   Name get name;
 }
 
 class DynamicInvocation extends InvocationExpression {
   final DynamicAccessKind kind;
   Expression receiver;
+
+  @override
   Name name;
+
+  @override
   Arguments arguments;
 
   DynamicInvocation(this.kind, this.receiver, this.name, this.arguments) {
-    receiver?.parent = this;
-    arguments?.parent = this;
+    receiver.parent = this;
+    arguments.parent = this;
   }
 
   @override
@@ -4357,27 +5044,47 @@
       case DynamicAccessKind.Unresolved:
         return const InvalidType();
     }
-    return const DynamicType();
   }
 
+  @override
   R accept<R>(ExpressionVisitor<R> v) => v.visitDynamicInvocation(this);
+
+  @override
   R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
       v.visitDynamicInvocation(this, arg);
 
-  visitChildren(Visitor v) {
-    receiver?.accept(v);
-    name?.accept(v);
-    arguments?.accept(v);
+  @override
+  void visitChildren(Visitor v) {
+    receiver.accept(v);
+    name.accept(v);
+    arguments.accept(v);
   }
 
-  transformChildren(Transformer v) {
+  @override
+  void transformChildren(Transformer v) {
+    // ignore: unnecessary_null_comparison
     if (receiver != null) {
-      receiver = receiver.accept<TreeNode>(v);
-      receiver?.parent = this;
+      receiver = v.transform(receiver);
+      receiver.parent = this;
     }
+    // ignore: unnecessary_null_comparison
     if (arguments != null) {
-      arguments = arguments.accept<TreeNode>(v);
-      arguments?.parent = this;
+      arguments = v.transform(arguments);
+      arguments.parent = this;
+    }
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    // ignore: unnecessary_null_comparison
+    if (receiver != null) {
+      receiver = v.transform(receiver);
+      receiver.parent = this;
+    }
+    // ignore: unnecessary_null_comparison
+    if (arguments != null) {
+      arguments = v.transform(arguments);
+      arguments.parent = this;
     }
   }
 
@@ -4458,8 +5165,13 @@
 
   final InstanceAccessKind kind;
   Expression receiver;
+
+  @override
   Name name;
+
+  @override
   Arguments arguments;
+
   int flags = 0;
 
   /// The static type of the invocation.
@@ -4481,25 +5193,29 @@
   Reference interfaceTargetReference;
 
   InstanceInvocation(InstanceAccessKind kind, Expression receiver, Name name,
-      Arguments arguments, {Member interfaceTarget, FunctionType functionType})
+      Arguments arguments,
+      {required Member interfaceTarget, required FunctionType functionType})
       : this.byReference(kind, receiver, name, arguments,
-            interfaceTargetReference: getMemberReferenceGetter(interfaceTarget),
+            interfaceTargetReference:
+                getNonNullableMemberReferenceGetter(interfaceTarget),
             functionType: functionType);
 
   InstanceInvocation.byReference(
       this.kind, this.receiver, this.name, this.arguments,
-      {this.interfaceTargetReference, this.functionType})
+      {required this.interfaceTargetReference, required this.functionType})
+      // ignore: unnecessary_null_comparison
       : assert(interfaceTargetReference != null),
+        // ignore: unnecessary_null_comparison
         assert(functionType != null),
         assert(functionType.typeParameters.isEmpty) {
-    receiver?.parent = this;
-    arguments?.parent = this;
+    receiver.parent = this;
+    arguments.parent = this;
   }
 
-  Member get interfaceTarget => interfaceTargetReference?.asMember;
+  Member get interfaceTarget => interfaceTargetReference.asMember;
 
   void set interfaceTarget(Member target) {
-    interfaceTargetReference = getMemberReferenceGetter(target);
+    interfaceTargetReference = getNonNullableMemberReferenceGetter(target);
   }
 
   /// If `true`, this call is known to be safe wrt. parameter covariance checks.
@@ -4537,28 +5253,50 @@
     flags = value ? (flags | FlagBoundsSafe) : (flags & ~FlagBoundsSafe);
   }
 
+  @override
   DartType getStaticTypeInternal(StaticTypeContext context) =>
       functionType.returnType;
 
+  @override
   R accept<R>(ExpressionVisitor<R> v) => v.visitInstanceInvocation(this);
+
+  @override
   R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
       v.visitInstanceInvocation(this, arg);
 
-  visitChildren(Visitor v) {
-    receiver?.accept(v);
-    interfaceTarget?.acceptReference(v);
-    name?.accept(v);
-    arguments?.accept(v);
+  @override
+  void visitChildren(Visitor v) {
+    receiver.accept(v);
+    interfaceTarget.acceptReference(v);
+    name.accept(v);
+    arguments.accept(v);
   }
 
-  transformChildren(Transformer v) {
+  @override
+  void transformChildren(Transformer v) {
+    // ignore: unnecessary_null_comparison
     if (receiver != null) {
-      receiver = receiver.accept<TreeNode>(v);
-      receiver?.parent = this;
+      receiver = v.transform(receiver);
+      receiver.parent = this;
     }
+    // ignore: unnecessary_null_comparison
     if (arguments != null) {
-      arguments = arguments.accept<TreeNode>(v);
-      arguments?.parent = this;
+      arguments = v.transform(arguments);
+      arguments.parent = this;
+    }
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    // ignore: unnecessary_null_comparison
+    if (receiver != null) {
+      receiver = v.transform(receiver);
+      receiver.parent = this;
+    }
+    // ignore: unnecessary_null_comparison
+    if (arguments != null) {
+      arguments = v.transform(arguments);
+      arguments.parent = this;
     }
   }
 
@@ -4629,6 +5367,7 @@
 
   Expression receiver;
 
+  @override
   Arguments arguments;
 
   /// The static type of the invocation.
@@ -4646,37 +5385,60 @@
   ///      local(0); // The function type is `int Function(int)`.
   ///    }
   ///
-  FunctionType functionType;
+  FunctionType? functionType;
 
   FunctionInvocation(this.kind, this.receiver, this.arguments,
-      {this.functionType}) {
-    receiver?.parent = this;
-    arguments?.parent = this;
+      {required this.functionType}) {
+    receiver.parent = this;
+    arguments.parent = this;
   }
 
+  @override
   Name get name => Name.callName;
 
+  @override
   DartType getStaticTypeInternal(StaticTypeContext context) =>
       functionType?.returnType ?? const DynamicType();
 
+  @override
   R accept<R>(ExpressionVisitor<R> v) => v.visitFunctionInvocation(this);
+
+  @override
   R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
       v.visitFunctionInvocation(this, arg);
 
-  visitChildren(Visitor v) {
-    receiver?.accept(v);
-    name?.accept(v);
-    arguments?.accept(v);
+  @override
+  void visitChildren(Visitor v) {
+    receiver.accept(v);
+    name.accept(v);
+    arguments.accept(v);
   }
 
-  transformChildren(Transformer v) {
+  @override
+  void transformChildren(Transformer v) {
+    // ignore: unnecessary_null_comparison
     if (receiver != null) {
-      receiver = receiver.accept<TreeNode>(v);
-      receiver?.parent = this;
+      receiver = v.transform(receiver);
+      receiver.parent = this;
     }
+    // ignore: unnecessary_null_comparison
     if (arguments != null) {
-      arguments = arguments.accept<TreeNode>(v);
-      arguments?.parent = this;
+      arguments = v.transform(arguments);
+      arguments.parent = this;
+    }
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    // ignore: unnecessary_null_comparison
+    if (receiver != null) {
+      receiver = v.transform(receiver);
+      receiver.parent = this;
+    }
+    // ignore: unnecessary_null_comparison
+    if (arguments != null) {
+      arguments = v.transform(arguments);
+      arguments.parent = this;
     }
   }
 
@@ -4697,6 +5459,8 @@
 class LocalFunctionInvocation extends InvocationExpression {
   /// The variable declaration for the function declaration.
   VariableDeclaration variable;
+
+  @override
   Arguments arguments;
 
   /// The static type of the invocation.
@@ -4713,29 +5477,47 @@
   ///
   FunctionType functionType;
 
-  LocalFunctionInvocation(this.variable, this.arguments, {this.functionType})
+  LocalFunctionInvocation(this.variable, this.arguments,
+      {required this.functionType})
+      // ignore: unnecessary_null_comparison
       : assert(functionType != null) {
-    arguments?.parent = this;
+    arguments.parent = this;
   }
 
+  @override
   Name get name => Name.callName;
 
   @override
   DartType getStaticTypeInternal(StaticTypeContext context) =>
       functionType.returnType;
 
+  @override
   R accept<R>(ExpressionVisitor<R> v) => v.visitLocalFunctionInvocation(this);
+
+  @override
   R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
       v.visitLocalFunctionInvocation(this, arg);
 
-  visitChildren(Visitor v) {
-    arguments?.accept(v);
+  @override
+  void visitChildren(Visitor v) {
+    arguments.accept(v);
   }
 
-  transformChildren(Transformer v) {
+  @override
+  void transformChildren(Transformer v) {
+    // ignore: unnecessary_null_comparison
     if (arguments != null) {
-      arguments = arguments.accept<TreeNode>(v);
-      arguments?.parent = this;
+      arguments = v.transform(arguments);
+      arguments.parent = this;
+    }
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    // ignore: unnecessary_null_comparison
+    if (arguments != null) {
+      arguments = v.transform(arguments);
+      arguments.parent = this;
     }
   }
 
@@ -4763,26 +5545,43 @@
   /// test.
   final bool isNot;
 
-  EqualsNull(this.expression, {this.isNot}) : assert(isNot != null) {
-    expression?.parent = this;
+  EqualsNull(this.expression, {required this.isNot})
+      // ignore: unnecessary_null_comparison
+      : assert(isNot != null) {
+    expression.parent = this;
   }
 
   @override
   DartType getStaticTypeInternal(StaticTypeContext context) =>
       context.typeEnvironment.coreTypes.boolRawType(context.nonNullable);
 
+  @override
   R accept<R>(ExpressionVisitor<R> v) => v.visitEqualsNull(this);
+
+  @override
   R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
       v.visitEqualsNull(this, arg);
 
-  visitChildren(Visitor v) {
-    expression?.accept(v);
+  @override
+  void visitChildren(Visitor v) {
+    expression.accept(v);
   }
 
-  transformChildren(Transformer v) {
+  @override
+  void transformChildren(Transformer v) {
+    // ignore: unnecessary_null_comparison
     if (expression != null) {
-      expression = expression.accept<TreeNode>(v);
-      expression?.parent = this;
+      expression = v.transform(expression);
+      expression.parent = this;
+    }
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    // ignore: unnecessary_null_comparison
+    if (expression != null) {
+      expression = v.transform(expression);
+      expression.parent = this;
     }
   }
 
@@ -4831,49 +5630,75 @@
   Reference interfaceTargetReference;
 
   EqualsCall(Expression left, Expression right,
-      {bool isNot, FunctionType functionType, Procedure interfaceTarget})
+      {required bool isNot,
+      required FunctionType functionType,
+      required Procedure interfaceTarget})
       : this.byReference(left, right,
             isNot: isNot,
             functionType: functionType,
             interfaceTargetReference:
-                getMemberReferenceGetter(interfaceTarget));
+                getNonNullableMemberReferenceGetter(interfaceTarget));
 
   EqualsCall.byReference(this.left, this.right,
-      {this.isNot, this.functionType, this.interfaceTargetReference})
+      {required this.isNot,
+      required this.functionType,
+      required this.interfaceTargetReference})
+      // ignore: unnecessary_null_comparison
       : assert(isNot != null) {
-    left?.parent = this;
-    right?.parent = this;
+    left.parent = this;
+    right.parent = this;
   }
 
-  Procedure get interfaceTarget => interfaceTargetReference?.asProcedure;
+  Procedure get interfaceTarget => interfaceTargetReference.asProcedure;
 
   void set interfaceTarget(Procedure target) {
-    interfaceTargetReference = getMemberReferenceGetter(target);
+    interfaceTargetReference = getNonNullableMemberReferenceGetter(target);
   }
 
+  @override
   DartType getStaticTypeInternal(StaticTypeContext context) {
-    return functionType?.returnType ??
-        context.typeEnvironment.coreTypes.boolRawType(context.nonNullable);
+    return functionType.returnType;
   }
 
+  @override
   R accept<R>(ExpressionVisitor<R> v) => v.visitEqualsCall(this);
+
+  @override
   R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
       v.visitEqualsCall(this, arg);
 
-  visitChildren(Visitor v) {
-    left?.accept(v);
-    interfaceTarget?.acceptReference(v);
-    right?.accept(v);
+  @override
+  void visitChildren(Visitor v) {
+    left.accept(v);
+    interfaceTarget.acceptReference(v);
+    right.accept(v);
   }
 
-  transformChildren(Transformer v) {
+  @override
+  void transformChildren(Transformer v) {
+    // ignore: unnecessary_null_comparison
     if (left != null) {
-      left = left.accept<TreeNode>(v);
-      left?.parent = this;
+      left = v.transform(left);
+      left.parent = this;
     }
+    // ignore: unnecessary_null_comparison
     if (right != null) {
-      right = right.accept<TreeNode>(v);
-      right?.parent = this;
+      right = v.transform(right);
+      right.parent = this;
+    }
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    // ignore: unnecessary_null_comparison
+    if (left != null) {
+      left = v.transform(left);
+      left.parent = this;
+    }
+    // ignore: unnecessary_null_comparison
+    if (right != null) {
+      right = v.transform(right);
+      right.parent = this;
     }
   }
 
@@ -4902,14 +5727,19 @@
   static const int FlagBoundsSafe = 1 << 1;
 
   Expression receiver;
+
+  @override
   Name name;
+
+  @override
   Arguments arguments;
+
   int flags = 0;
 
-  Reference interfaceTargetReference;
+  Reference? interfaceTargetReference;
 
   MethodInvocation(Expression receiver, Name name, Arguments arguments,
-      [Member interfaceTarget])
+      [Member? interfaceTarget])
       : this.byReference(
             receiver,
             name,
@@ -4919,13 +5749,13 @@
 
   MethodInvocation.byReference(
       this.receiver, this.name, this.arguments, this.interfaceTargetReference) {
-    receiver?.parent = this;
-    arguments?.parent = this;
+    receiver.parent = this;
+    arguments.parent = this;
   }
 
-  Member get interfaceTarget => interfaceTargetReference?.asMember;
+  Member? get interfaceTarget => interfaceTargetReference?.asMember;
 
-  void set interfaceTarget(Member target) {
+  void set interfaceTarget(Member? target) {
     // An invocation doesn't refer to the setter.
     interfaceTargetReference = getMemberReferenceGetter(target);
   }
@@ -4965,8 +5795,9 @@
     flags = value ? (flags | FlagBoundsSafe) : (flags & ~FlagBoundsSafe);
   }
 
+  @override
   DartType getStaticTypeInternal(StaticTypeContext context) {
-    Member interfaceTarget = this.interfaceTarget;
+    Member? interfaceTarget = this.interfaceTarget;
     if (interfaceTarget != null) {
       if (interfaceTarget is Procedure &&
           context.typeEnvironment
@@ -4975,8 +5806,8 @@
             receiver.getStaticType(context),
             arguments.positional[0].getStaticType(context));
       }
-      Class superclass = interfaceTarget.enclosingClass;
-      DartType receiverType =
+      Class superclass = interfaceTarget.enclosingClass!;
+      InterfaceType receiverType =
           receiver.getStaticTypeAsInstanceOf(superclass, context);
       DartType getterType = Substitution.fromInterfaceType(receiverType)
           .substituteType(interfaceTarget.getterType);
@@ -4993,7 +5824,7 @@
               getterType.typeParameters,
               getterType.typeParameters
                   .map((TypeParameter typeParameter) =>
-                      typeParameter.defaultType)
+                      typeParameter.defaultType!)
                   .toList());
         }
         return substitution.substituteType(getterType.returnType);
@@ -5004,7 +5835,7 @@
       // TODO(johnniwinther): Remove this when the front end performs the
       // correct replacement.
       if (getterType is InterfaceType) {
-        Member member = context.typeEnvironment
+        Member? member = context.typeEnvironment
             .getInterfaceMember(getterType.classNode, new Name('call'));
         if (member != null) {
           DartType callType = member.getterType;
@@ -5034,25 +5865,46 @@
     return const DynamicType();
   }
 
+  @override
   R accept<R>(ExpressionVisitor<R> v) => v.visitMethodInvocation(this);
+
+  @override
   R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
       v.visitMethodInvocation(this, arg);
 
-  visitChildren(Visitor v) {
-    receiver?.accept(v);
+  @override
+  void visitChildren(Visitor v) {
+    receiver.accept(v);
     interfaceTarget?.acceptReference(v);
-    name?.accept(v);
-    arguments?.accept(v);
+    name.accept(v);
+    arguments.accept(v);
   }
 
-  transformChildren(Transformer v) {
+  @override
+  void transformChildren(Transformer v) {
+    // ignore: unnecessary_null_comparison
     if (receiver != null) {
-      receiver = receiver.accept<TreeNode>(v);
-      receiver?.parent = this;
+      receiver = v.transform(receiver);
+      receiver.parent = this;
     }
+    // ignore: unnecessary_null_comparison
     if (arguments != null) {
-      arguments = arguments.accept<TreeNode>(v);
-      arguments?.parent = this;
+      arguments = v.transform(arguments);
+      arguments.parent = this;
+    }
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    // ignore: unnecessary_null_comparison
+    if (receiver != null) {
+      receiver = v.transform(receiver);
+      receiver.parent = this;
+    }
+    // ignore: unnecessary_null_comparison
+    if (arguments != null) {
+      arguments = v.transform(arguments);
+      arguments.parent = this;
     }
   }
 
@@ -5075,13 +5927,16 @@
 ///
 /// The provided arguments might not match the parameters of the target.
 class SuperMethodInvocation extends InvocationExpression {
+  @override
   Name name;
+
+  @override
   Arguments arguments;
 
-  Reference interfaceTargetReference;
+  Reference? interfaceTargetReference;
 
   SuperMethodInvocation(Name name, Arguments arguments,
-      [Procedure interfaceTarget])
+      [Procedure? interfaceTarget])
       : this.byReference(
             name,
             arguments,
@@ -5090,43 +5945,60 @@
 
   SuperMethodInvocation.byReference(
       this.name, this.arguments, this.interfaceTargetReference) {
-    arguments?.parent = this;
+    arguments.parent = this;
   }
 
-  Procedure get interfaceTarget => interfaceTargetReference?.asProcedure;
+  Procedure? get interfaceTarget => interfaceTargetReference?.asProcedure;
 
-  void set interfaceTarget(Procedure target) {
+  void set interfaceTarget(Procedure? target) {
     // An invocation doesn't refer to the setter.
     interfaceTargetReference = getMemberReferenceGetter(target);
   }
 
+  @override
   DartType getStaticTypeInternal(StaticTypeContext context) {
+    Procedure? interfaceTarget = this.interfaceTarget;
     if (interfaceTarget == null) return const DynamicType();
-    Class superclass = interfaceTarget.enclosingClass;
-    List<DartType> receiverTypeArguments = context.typeEnvironment
-        .getTypeArgumentsAsInstanceOf(context.thisType, superclass);
-    DartType returnType =
-        Substitution.fromPairs(superclass.typeParameters, receiverTypeArguments)
-            .substituteType(interfaceTarget.function.returnType);
+    Class superclass = interfaceTarget.enclosingClass!;
+    List<DartType>? receiverTypeArguments = context.typeEnvironment
+        .getTypeArgumentsAsInstanceOf(context.thisType!, superclass);
+    DartType returnType = Substitution.fromPairs(
+            superclass.typeParameters, receiverTypeArguments!)
+        .substituteType(interfaceTarget.function!.returnType);
     return Substitution.fromPairs(
-            interfaceTarget.function.typeParameters, arguments.types)
+            interfaceTarget.function!.typeParameters, arguments.types)
         .substituteType(returnType);
   }
 
+  @override
   R accept<R>(ExpressionVisitor<R> v) => v.visitSuperMethodInvocation(this);
+
+  @override
   R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
       v.visitSuperMethodInvocation(this, arg);
 
-  visitChildren(Visitor v) {
+  @override
+  void visitChildren(Visitor v) {
     interfaceTarget?.acceptReference(v);
-    name?.accept(v);
-    arguments?.accept(v);
+    name.accept(v);
+    arguments.accept(v);
   }
 
-  transformChildren(Transformer v) {
+  @override
+  void transformChildren(Transformer v) {
+    // ignore: unnecessary_null_comparison
     if (arguments != null) {
-      arguments = arguments.accept<TreeNode>(v);
-      arguments?.parent = this;
+      arguments = v.transform(arguments);
+      arguments.parent = this;
+    }
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    // ignore: unnecessary_null_comparison
+    if (arguments != null) {
+      arguments = v.transform(arguments);
+      arguments.parent = this;
     }
   }
 
@@ -5149,51 +6021,70 @@
 /// The provided arguments might not match the parameters of the target.
 class StaticInvocation extends InvocationExpression {
   Reference targetReference;
+
+  @override
   Arguments arguments;
 
   /// True if this is a constant call to an external constant factory.
   bool isConst;
 
-  Name get name => target?.name;
+  @override
+  Name get name => target.name!;
 
   StaticInvocation(Procedure target, Arguments arguments, {bool isConst: false})
       : this.byReference(
             // An invocation doesn't refer to the setter.
-            getMemberReferenceGetter(target),
+            getNonNullableMemberReferenceGetter(target),
             arguments,
             isConst: isConst);
 
   StaticInvocation.byReference(this.targetReference, this.arguments,
       {this.isConst: false}) {
-    arguments?.parent = this;
+    arguments.parent = this;
   }
 
-  Procedure get target => targetReference?.asProcedure;
+  Procedure get target => targetReference.asProcedure;
 
   void set target(Procedure target) {
     // An invocation doesn't refer to the setter.
-    targetReference = getMemberReferenceGetter(target);
+    targetReference = getNonNullableMemberReferenceGetter(target);
   }
 
+  @override
   DartType getStaticTypeInternal(StaticTypeContext context) {
     return Substitution.fromPairs(
-            target.function.typeParameters, arguments.types)
-        .substituteType(target.function.returnType);
+            target.function!.typeParameters, arguments.types)
+        .substituteType(target.function!.returnType);
   }
 
+  @override
   R accept<R>(ExpressionVisitor<R> v) => v.visitStaticInvocation(this);
+
+  @override
   R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
       v.visitStaticInvocation(this, arg);
 
-  visitChildren(Visitor v) {
-    target?.acceptReference(v);
-    arguments?.accept(v);
+  @override
+  void visitChildren(Visitor v) {
+    target.acceptReference(v);
+    arguments.accept(v);
   }
 
-  transformChildren(Transformer v) {
+  @override
+  void transformChildren(Transformer v) {
+    // ignore: unnecessary_null_comparison
     if (arguments != null) {
-      arguments = arguments.accept<TreeNode>(v);
-      arguments?.parent = this;
+      arguments = v.transform(arguments);
+      arguments.parent = this;
+    }
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    // ignore: unnecessary_null_comparison
+    if (arguments != null) {
+      arguments = v.transform(arguments);
+      arguments.parent = this;
     }
   }
 
@@ -5218,58 +6109,78 @@
 // generic functions.
 class ConstructorInvocation extends InvocationExpression {
   Reference targetReference;
+
+  @override
   Arguments arguments;
+
   bool isConst;
 
-  Name get name => target?.name;
+  @override
+  Name get name => target.name!;
 
   ConstructorInvocation(Constructor target, Arguments arguments,
       {bool isConst: false})
       : this.byReference(
             // A constructor doesn't refer to the setter.
-            getMemberReferenceGetter(target),
+            getNonNullableMemberReferenceGetter(target),
             arguments,
             isConst: isConst);
 
   ConstructorInvocation.byReference(this.targetReference, this.arguments,
       {this.isConst: false}) {
-    arguments?.parent = this;
+    arguments.parent = this;
   }
 
-  Constructor get target => targetReference?.asConstructor;
+  Constructor get target => targetReference.asConstructor;
 
   void set target(Constructor target) {
     // A constructor doesn't refer to the setter.
-    targetReference = getMemberReferenceGetter(target);
+    targetReference = getNonNullableMemberReferenceGetter(target);
   }
 
+  @override
   DartType getStaticTypeInternal(StaticTypeContext context) {
     return arguments.types.isEmpty
         ? context.typeEnvironment.coreTypes
-            .rawType(target.enclosingClass, context.nonNullable)
+            .rawType(target.enclosingClass!, context.nonNullable)
         : new InterfaceType(
-            target.enclosingClass, context.nonNullable, arguments.types);
+            target.enclosingClass!, context.nonNullable, arguments.types);
   }
 
+  @override
   R accept<R>(ExpressionVisitor<R> v) => v.visitConstructorInvocation(this);
+
+  @override
   R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
       v.visitConstructorInvocation(this, arg);
 
-  visitChildren(Visitor v) {
-    target?.acceptReference(v);
-    arguments?.accept(v);
+  @override
+  void visitChildren(Visitor v) {
+    target.acceptReference(v);
+    arguments.accept(v);
   }
 
-  transformChildren(Transformer v) {
+  @override
+  void transformChildren(Transformer v) {
+    // ignore: unnecessary_null_comparison
     if (arguments != null) {
-      arguments = arguments.accept<TreeNode>(v);
-      arguments?.parent = this;
+      arguments = v.transform(arguments);
+      arguments.parent = this;
+    }
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    // ignore: unnecessary_null_comparison
+    if (arguments != null) {
+      arguments = v.transform(arguments);
+      arguments.parent = this;
     }
   }
 
   // TODO(dmitryas): Change the getter into a method that accepts a CoreTypes.
   InterfaceType get constructedType {
-    Class enclosingClass = target.enclosingClass;
+    Class enclosingClass = target.enclosingClass!;
     // TODO(dmitryas): Get raw type from a CoreTypes object if arguments is
     // empty.
     return arguments.types.isEmpty
@@ -5291,11 +6202,11 @@
     } else {
       printer.write('new ');
     }
-    printer.writeClassName(target.enclosingClass.reference);
+    printer.writeClassName(target.enclosingClass!.reference);
     printer.writeTypeArguments(arguments.types);
-    if (target.name.text.isNotEmpty) {
+    if (target.name!.text.isNotEmpty) {
       printer.write('.');
-      printer.write(target.name.text);
+      printer.write(target.name!.text);
     }
     printer.writeArguments(arguments, includeTypeArguments: false);
   }
@@ -5307,30 +6218,47 @@
   final List<DartType> typeArguments;
 
   Instantiation(this.expression, this.typeArguments) {
-    expression?.parent = this;
+    expression.parent = this;
   }
 
+  @override
   DartType getStaticTypeInternal(StaticTypeContext context) {
-    FunctionType type = expression.getStaticType(context);
+    FunctionType type = expression.getStaticType(context) as FunctionType;
     return Substitution.fromPairs(type.typeParameters, typeArguments)
         .substituteType(type.withoutTypeParameters);
   }
 
+  @override
   R accept<R>(ExpressionVisitor<R> v) => v.visitInstantiation(this);
+
+  @override
   R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
       v.visitInstantiation(this, arg);
 
-  visitChildren(Visitor v) {
-    expression?.accept(v);
+  @override
+  void visitChildren(Visitor v) {
+    expression.accept(v);
     visitList(typeArguments, v);
   }
 
-  transformChildren(Transformer v) {
+  @override
+  void transformChildren(Transformer v) {
+    // ignore: unnecessary_null_comparison
     if (expression != null) {
-      expression = expression.accept<TreeNode>(v);
-      expression?.parent = this;
+      expression = v.transform(expression);
+      expression.parent = this;
     }
-    transformTypeList(typeArguments, v);
+    v.transformDartTypeList(typeArguments);
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    // ignore: unnecessary_null_comparison
+    if (expression != null) {
+      expression = v.transform(expression);
+      expression.parent = this;
+    }
+    v.transformDartTypeList(typeArguments);
   }
 
   @override
@@ -5353,9 +6281,10 @@
   Expression operand;
 
   Not(this.operand) {
-    operand?.parent = this;
+    operand.parent = this;
   }
 
+  @override
   DartType getStaticType(StaticTypeContext context) =>
       getStaticTypeInternal(context);
 
@@ -5363,17 +6292,32 @@
   DartType getStaticTypeInternal(StaticTypeContext context) =>
       context.typeEnvironment.coreTypes.boolRawType(context.nonNullable);
 
+  @override
   R accept<R>(ExpressionVisitor<R> v) => v.visitNot(this);
+
+  @override
   R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => v.visitNot(this, arg);
 
-  visitChildren(Visitor v) {
-    operand?.accept(v);
+  @override
+  void visitChildren(Visitor v) {
+    operand.accept(v);
   }
 
-  transformChildren(Transformer v) {
+  @override
+  void transformChildren(Transformer v) {
+    // ignore: unnecessary_null_comparison
     if (operand != null) {
-      operand = operand.accept<TreeNode>(v);
-      operand?.parent = this;
+      operand = v.transform(operand);
+      operand.parent = this;
+    }
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    // ignore: unnecessary_null_comparison
+    if (operand != null) {
+      operand = v.transform(operand);
+      operand.parent = this;
     }
   }
 
@@ -5399,7 +6343,6 @@
     case LogicalExpressionOperator.OR:
       return "||";
   }
-  throw "Unhandled LogicalExpressionOperator: ${operator}";
 }
 
 /// Expression of form `x && y` or `x || y`
@@ -5409,10 +6352,11 @@
   Expression right;
 
   LogicalExpression(this.left, this.operatorEnum, this.right) {
-    left?.parent = this;
-    right?.parent = this;
+    left.parent = this;
+    right.parent = this;
   }
 
+  @override
   DartType getStaticType(StaticTypeContext context) =>
       getStaticTypeInternal(context);
 
@@ -5420,23 +6364,44 @@
   DartType getStaticTypeInternal(StaticTypeContext context) =>
       context.typeEnvironment.coreTypes.boolRawType(context.nonNullable);
 
+  @override
   R accept<R>(ExpressionVisitor<R> v) => v.visitLogicalExpression(this);
+
+  @override
   R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
       v.visitLogicalExpression(this, arg);
 
-  visitChildren(Visitor v) {
-    left?.accept(v);
-    right?.accept(v);
+  @override
+  void visitChildren(Visitor v) {
+    left.accept(v);
+    right.accept(v);
   }
 
-  transformChildren(Transformer v) {
+  @override
+  void transformChildren(Transformer v) {
+    // ignore: unnecessary_null_comparison
     if (left != null) {
-      left = left.accept<TreeNode>(v);
-      left?.parent = this;
+      left = v.transform(left);
+      left.parent = this;
     }
+    // ignore: unnecessary_null_comparison
     if (right != null) {
-      right = right.accept<TreeNode>(v);
-      right?.parent = this;
+      right = v.transform(right);
+      right.parent = this;
+    }
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    // ignore: unnecessary_null_comparison
+    if (left != null) {
+      left = v.transform(left);
+      left.parent = this;
+    }
+    // ignore: unnecessary_null_comparison
+    if (right != null) {
+      right = v.transform(right);
+      right.parent = this;
     }
   }
 
@@ -5460,52 +6425,85 @@
   Expression then;
   Expression otherwise;
 
-  /// The static type of the expression. Should not be `null`.
+  /// The static type of the expression.
   DartType staticType;
 
   ConditionalExpression(
       this.condition, this.then, this.otherwise, this.staticType) {
-    condition?.parent = this;
-    then?.parent = this;
-    otherwise?.parent = this;
+    condition.parent = this;
+    then.parent = this;
+    otherwise.parent = this;
   }
 
+  @override
   DartType getStaticType(StaticTypeContext context) =>
       getStaticTypeInternal(context);
 
   @override
   DartType getStaticTypeInternal(StaticTypeContext context) => staticType;
 
+  @override
   R accept<R>(ExpressionVisitor<R> v) => v.visitConditionalExpression(this);
+
+  @override
   R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
       v.visitConditionalExpression(this, arg);
 
-  visitChildren(Visitor v) {
-    condition?.accept(v);
-    then?.accept(v);
-    otherwise?.accept(v);
-    staticType?.accept(v);
+  @override
+  void visitChildren(Visitor v) {
+    condition.accept(v);
+    then.accept(v);
+    otherwise.accept(v);
+    staticType.accept(v);
   }
 
-  transformChildren(Transformer v) {
+  @override
+  void transformChildren(Transformer v) {
+    // ignore: unnecessary_null_comparison
     if (condition != null) {
-      condition = condition.accept<TreeNode>(v);
-      condition?.parent = this;
+      condition = v.transform(condition);
+      condition.parent = this;
     }
+    // ignore: unnecessary_null_comparison
     if (then != null) {
-      then = then.accept<TreeNode>(v);
-      then?.parent = this;
+      then = v.transform(then);
+      then.parent = this;
     }
+    // ignore: unnecessary_null_comparison
     if (otherwise != null) {
-      otherwise = otherwise.accept<TreeNode>(v);
-      otherwise?.parent = this;
+      otherwise = v.transform(otherwise);
+      otherwise.parent = this;
     }
+    // ignore: unnecessary_null_comparison
     if (staticType != null) {
       staticType = v.visitDartType(staticType);
     }
   }
 
   @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    // ignore: unnecessary_null_comparison
+    if (condition != null) {
+      condition = v.transform(condition);
+      condition.parent = this;
+    }
+    // ignore: unnecessary_null_comparison
+    if (then != null) {
+      then = v.transform(then);
+      then.parent = this;
+    }
+    // ignore: unnecessary_null_comparison
+    if (otherwise != null) {
+      otherwise = v.transform(otherwise);
+      otherwise.parent = this;
+    }
+    // ignore: unnecessary_null_comparison
+    if (staticType != null) {
+      staticType = v.visitDartType(staticType, cannotRemoveSentinel);
+    }
+  }
+
+  @override
   String toString() {
     return "ConditionalExpression(${toStringInternal()})";
   }
@@ -5515,6 +6513,7 @@
     printer.writeExpression(condition,
         minimumPrecedence: astToText.Precedence.LOGICAL_OR);
     printer.write(' ?');
+    // ignore: unnecessary_null_comparison
     if (staticType != null) {
       printer.write('{');
       printer.writeType(staticType);
@@ -5541,6 +6540,7 @@
     setParents(expressions, this);
   }
 
+  @override
   DartType getStaticType(StaticTypeContext context) =>
       getStaticTypeInternal(context);
 
@@ -5548,16 +6548,26 @@
   DartType getStaticTypeInternal(StaticTypeContext context) =>
       context.typeEnvironment.coreTypes.stringRawType(context.nonNullable);
 
+  @override
   R accept<R>(ExpressionVisitor<R> v) => v.visitStringConcatenation(this);
+
+  @override
   R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
       v.visitStringConcatenation(this, arg);
 
-  visitChildren(Visitor v) {
+  @override
+  void visitChildren(Visitor v) {
     visitList(expressions, v);
   }
 
-  transformChildren(Transformer v) {
-    transformList(expressions, v, this);
+  @override
+  void transformChildren(Transformer v) {
+    v.transformList(expressions, this);
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    v.transformExpressionList(expressions, this);
   }
 
   @override
@@ -5596,6 +6606,7 @@
     setParents(lists, this);
   }
 
+  @override
   DartType getStaticType(StaticTypeContext context) =>
       getStaticTypeInternal(context);
 
@@ -5604,18 +6615,29 @@
     return context.typeEnvironment.listType(typeArgument, context.nonNullable);
   }
 
+  @override
   R accept<R>(ExpressionVisitor<R> v) => v.visitListConcatenation(this);
+
+  @override
   R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
       v.visitListConcatenation(this, arg);
 
-  visitChildren(Visitor v) {
-    typeArgument?.accept(v);
+  @override
+  void visitChildren(Visitor v) {
+    typeArgument.accept(v);
     visitList(lists, v);
   }
 
-  transformChildren(Transformer v) {
+  @override
+  void transformChildren(Transformer v) {
     typeArgument = v.visitDartType(typeArgument);
-    transformList(lists, v, this);
+    v.transformList(lists, this);
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    typeArgument = v.visitDartType(typeArgument, cannotRemoveSentinel);
+    v.transformExpressionList(lists, this);
   }
 
   @override
@@ -5654,6 +6676,7 @@
     setParents(sets, this);
   }
 
+  @override
   DartType getStaticType(StaticTypeContext context) =>
       getStaticTypeInternal(context);
 
@@ -5662,18 +6685,29 @@
     return context.typeEnvironment.setType(typeArgument, context.nonNullable);
   }
 
+  @override
   R accept<R>(ExpressionVisitor<R> v) => v.visitSetConcatenation(this);
+
+  @override
   R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
       v.visitSetConcatenation(this, arg);
 
-  visitChildren(Visitor v) {
-    typeArgument?.accept(v);
+  @override
+  void visitChildren(Visitor v) {
+    typeArgument.accept(v);
     visitList(sets, v);
   }
 
-  transformChildren(Transformer v) {
+  @override
+  void transformChildren(Transformer v) {
     typeArgument = v.visitDartType(typeArgument);
-    transformList(sets, v, this);
+    v.transformList(sets, this);
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    typeArgument = v.visitDartType(typeArgument, cannotRemoveSentinel);
+    v.transformExpressionList(sets, this);
   }
 
   @override
@@ -5715,6 +6749,7 @@
     setParents(maps, this);
   }
 
+  @override
   DartType getStaticType(StaticTypeContext context) =>
       getStaticTypeInternal(context);
 
@@ -5724,20 +6759,32 @@
         .mapType(keyType, valueType, context.nonNullable);
   }
 
+  @override
   R accept<R>(ExpressionVisitor<R> v) => v.visitMapConcatenation(this);
+
+  @override
   R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
       v.visitMapConcatenation(this, arg);
 
-  visitChildren(Visitor v) {
-    keyType?.accept(v);
-    valueType?.accept(v);
+  @override
+  void visitChildren(Visitor v) {
+    keyType.accept(v);
+    valueType.accept(v);
     visitList(maps, v);
   }
 
-  transformChildren(Transformer v) {
+  @override
+  void transformChildren(Transformer v) {
     keyType = v.visitDartType(keyType);
     valueType = v.visitDartType(valueType);
-    transformList(maps, v, this);
+    v.transformList(maps, this);
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    keyType = v.visitDartType(keyType, cannotRemoveSentinel);
+    valueType = v.visitDartType(valueType, cannotRemoveSentinel);
+    v.transformExpressionList(maps, this);
   }
 
   @override
@@ -5780,6 +6827,7 @@
 
   Class get classNode => classReference.asClass;
 
+  @override
   DartType getStaticType(StaticTypeContext context) =>
       getStaticTypeInternal(context);
 
@@ -5791,11 +6839,15 @@
         : new InterfaceType(classNode, context.nonNullable, typeArguments);
   }
 
+  @override
   R accept<R>(ExpressionVisitor<R> v) => v.visitInstanceCreation(this);
+
+  @override
   R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
       v.visitInstanceCreation(this, arg);
 
-  visitChildren(Visitor v) {
+  @override
+  void visitChildren(Visitor v) {
     classReference.asClass.acceptReference(v);
     visitList(typeArguments, v);
     for (final Reference reference in fieldValues.keys) {
@@ -5808,16 +6860,31 @@
     visitList(unusedArguments, v);
   }
 
-  transformChildren(Transformer v) {
+  @override
+  void transformChildren(Transformer v) {
     fieldValues.forEach((Reference fieldRef, Expression value) {
-      Expression transformed = value.accept<TreeNode>(v);
+      Expression transformed = v.transform(value);
+      // ignore: unnecessary_null_comparison
       if (transformed != null && !identical(value, transformed)) {
         fieldValues[fieldRef] = transformed;
         transformed.parent = this;
       }
     });
-    transformList(asserts, v, this);
-    transformList(unusedArguments, v, this);
+    v.transformList(asserts, this);
+    v.transformList(unusedArguments, this);
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    fieldValues.forEach((Reference fieldRef, Expression value) {
+      Expression transformed = v.transform(value);
+      if (!identical(value, transformed)) {
+        fieldValues[fieldRef] = transformed;
+        transformed.parent = this;
+      }
+    });
+    v.transformList(asserts, this, dummyAssertStatement);
+    v.transformExpressionList(unusedArguments, this);
   }
 
   @override
@@ -5848,7 +6915,7 @@
       printer.writeExpression(assert_.condition);
       if (assert_.message != null) {
         printer.write(', ');
-        printer.writeExpression(assert_.message);
+        printer.writeExpression(assert_.message!);
       }
       printer.write(')');
       first = false;
@@ -5881,6 +6948,7 @@
     expression.parent = this;
   }
 
+  @override
   DartType getStaticType(StaticTypeContext context) =>
       getStaticTypeInternal(context);
 
@@ -5888,19 +6956,30 @@
   DartType getStaticTypeInternal(StaticTypeContext context) =>
       expression.getStaticType(context);
 
+  @override
   R accept<R>(ExpressionVisitor<R> v) => v.visitFileUriExpression(this);
+
+  @override
   R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
       v.visitFileUriExpression(this, arg);
 
-  visitChildren(Visitor v) {
+  @override
+  void visitChildren(Visitor v) {
     expression.accept(v);
   }
 
-  transformChildren(Transformer v) {
-    expression = expression.accept<TreeNode>(v)..parent = this;
+  @override
+  void transformChildren(Transformer v) {
+    expression = v.transform(expression)..parent = this;
   }
 
-  Location _getLocationInEnclosingFile(int offset) {
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    expression = v.transform(expression)..parent = this;
+  }
+
+  @override
+  Location? _getLocationInEnclosingFile(int offset) {
     return _getLocationInComponent(enclosingComponent, fileUri, offset);
   }
 
@@ -5927,7 +7006,7 @@
   DartType type;
 
   IsExpression(this.operand, this.type) {
-    operand?.parent = this;
+    operand.parent = this;
   }
 
   // Must match serialized bit positions.
@@ -5953,24 +7032,40 @@
   DartType getStaticTypeInternal(StaticTypeContext context) =>
       context.typeEnvironment.coreTypes.boolRawType(context.nonNullable);
 
+  @override
   R accept<R>(ExpressionVisitor<R> v) => v.visitIsExpression(this);
+
+  @override
   R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
       v.visitIsExpression(this, arg);
 
-  visitChildren(Visitor v) {
-    operand?.accept(v);
-    type?.accept(v);
+  @override
+  void visitChildren(Visitor v) {
+    operand.accept(v);
+    type.accept(v);
   }
 
-  transformChildren(Transformer v) {
+  @override
+  void transformChildren(Transformer v) {
+    // ignore: unnecessary_null_comparison
     if (operand != null) {
-      operand = operand.accept<TreeNode>(v);
-      operand?.parent = this;
+      operand = v.transform(operand);
+      operand.parent = this;
     }
     type = v.visitDartType(type);
   }
 
   @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    // ignore: unnecessary_null_comparison
+    if (operand != null) {
+      operand = v.transform(operand);
+      operand.parent = this;
+    }
+    type = v.visitDartType(type, cannotRemoveSentinel);
+  }
+
+  @override
   String toString() {
     return "IsExpression(${toStringInternal()})";
   }
@@ -5995,7 +7090,7 @@
   DartType type;
 
   AsExpression(this.operand, this.type) {
-    operand?.parent = this;
+    operand.parent = this;
   }
 
   // Must match serialized bit positions.
@@ -6060,30 +7155,47 @@
         : (flags & ~FlagForNonNullableByDefault);
   }
 
+  @override
   DartType getStaticType(StaticTypeContext context) =>
       getStaticTypeInternal(context);
 
   @override
   DartType getStaticTypeInternal(StaticTypeContext context) => type;
 
+  @override
   R accept<R>(ExpressionVisitor<R> v) => v.visitAsExpression(this);
+
+  @override
   R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
       v.visitAsExpression(this, arg);
 
-  visitChildren(Visitor v) {
-    operand?.accept(v);
-    type?.accept(v);
+  @override
+  void visitChildren(Visitor v) {
+    operand.accept(v);
+    type.accept(v);
   }
 
-  transformChildren(Transformer v) {
+  @override
+  void transformChildren(Transformer v) {
+    // ignore: unnecessary_null_comparison
     if (operand != null) {
-      operand = operand.accept<TreeNode>(v);
-      operand?.parent = this;
+      operand = v.transform(operand);
+      operand.parent = this;
     }
     type = v.visitDartType(type);
   }
 
   @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    // ignore: unnecessary_null_comparison
+    if (operand != null) {
+      operand = v.transform(operand);
+      operand.parent = this;
+    }
+    type = v.visitDartType(type, cannotRemoveSentinel);
+  }
+
+  @override
   String toString() {
     return "AsExpression(${toStringInternal()})";
   }
@@ -6124,9 +7236,10 @@
   Expression operand;
 
   NullCheck(this.operand) {
-    operand?.parent = this;
+    operand.parent = this;
   }
 
+  @override
   DartType getStaticTypeInternal(StaticTypeContext context) {
     DartType operandType = operand.getStaticType(context);
     return operandType is NullType
@@ -6134,18 +7247,33 @@
         : operandType.withDeclaredNullability(Nullability.nonNullable);
   }
 
+  @override
   R accept<R>(ExpressionVisitor<R> v) => v.visitNullCheck(this);
+
+  @override
   R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
       v.visitNullCheck(this, arg);
 
-  visitChildren(Visitor v) {
-    operand?.accept(v);
+  @override
+  void visitChildren(Visitor v) {
+    operand.accept(v);
   }
 
-  transformChildren(Transformer v) {
+  @override
+  void transformChildren(Transformer v) {
+    // ignore: unnecessary_null_comparison
     if (operand != null) {
-      operand = operand.accept<TreeNode>(v);
-      operand?.parent = this;
+      operand = v.transform(operand);
+      operand.parent = this;
+    }
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    // ignore: unnecessary_null_comparison
+    if (operand != null) {
+      operand = v.transform(operand);
+      operand.parent = this;
     }
   }
 
@@ -6164,17 +7292,25 @@
 
 /// An integer, double, boolean, string, or null constant.
 abstract class BasicLiteral extends Expression {
-  Object get value;
+  Object? get value;
 
-  visitChildren(Visitor v) {}
-  transformChildren(Transformer v) {}
+  @override
+  void visitChildren(Visitor v) {}
+
+  @override
+  void transformChildren(Transformer v) {}
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {}
 }
 
 class StringLiteral extends BasicLiteral {
+  @override
   String value;
 
   StringLiteral(this.value);
 
+  @override
   DartType getStaticType(StaticTypeContext context) =>
       getStaticTypeInternal(context);
 
@@ -6182,7 +7318,10 @@
   DartType getStaticTypeInternal(StaticTypeContext context) =>
       context.typeEnvironment.coreTypes.stringRawType(context.nonNullable);
 
+  @override
   R accept<R>(ExpressionVisitor<R> v) => v.visitStringLiteral(this);
+
+  @override
   R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
       v.visitStringLiteral(this, arg);
 
@@ -6204,10 +7343,12 @@
   /// E.g. "0x8000000000000000" will be saved as "-9223372036854775808" despite
   /// technically (on some platforms, particularly Javascript) being positive.
   /// If the number is meant to be negative it will be wrapped in a "unary-".
+  @override
   int value;
 
   IntLiteral(this.value);
 
+  @override
   DartType getStaticType(StaticTypeContext context) =>
       getStaticTypeInternal(context);
 
@@ -6215,7 +7356,10 @@
   DartType getStaticTypeInternal(StaticTypeContext context) =>
       context.typeEnvironment.coreTypes.intRawType(context.nonNullable);
 
+  @override
   R accept<R>(ExpressionVisitor<R> v) => v.visitIntLiteral(this);
+
+  @override
   R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
       v.visitIntLiteral(this, arg);
 
@@ -6231,10 +7375,12 @@
 }
 
 class DoubleLiteral extends BasicLiteral {
+  @override
   double value;
 
   DoubleLiteral(this.value);
 
+  @override
   DartType getStaticType(StaticTypeContext context) =>
       getStaticTypeInternal(context);
 
@@ -6242,7 +7388,10 @@
   DartType getStaticTypeInternal(StaticTypeContext context) =>
       context.typeEnvironment.coreTypes.doubleRawType(context.nonNullable);
 
+  @override
   R accept<R>(ExpressionVisitor<R> v) => v.visitDoubleLiteral(this);
+
+  @override
   R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
       v.visitDoubleLiteral(this, arg);
 
@@ -6258,10 +7407,12 @@
 }
 
 class BoolLiteral extends BasicLiteral {
+  @override
   bool value;
 
   BoolLiteral(this.value);
 
+  @override
   DartType getStaticType(StaticTypeContext context) =>
       getStaticTypeInternal(context);
 
@@ -6269,7 +7420,10 @@
   DartType getStaticTypeInternal(StaticTypeContext context) =>
       context.typeEnvironment.coreTypes.boolRawType(context.nonNullable);
 
+  @override
   R accept<R>(ExpressionVisitor<R> v) => v.visitBoolLiteral(this);
+
+  @override
   R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
       v.visitBoolLiteral(this, arg);
 
@@ -6285,7 +7439,8 @@
 }
 
 class NullLiteral extends BasicLiteral {
-  Object get value => null;
+  @override
+  Object? get value => null;
 
   DartType getStaticType(StaticTypeContext context) =>
       getStaticTypeInternal(context);
@@ -6293,7 +7448,10 @@
   @override
   DartType getStaticTypeInternal(StaticTypeContext context) => const NullType();
 
+  @override
   R accept<R>(ExpressionVisitor<R> v) => v.visitNullLiteral(this);
+
+  @override
   R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
       v.visitNullLiteral(this, arg);
 
@@ -6313,6 +7471,7 @@
 
   SymbolLiteral(this.value);
 
+  @override
   DartType getStaticType(StaticTypeContext context) =>
       getStaticTypeInternal(context);
 
@@ -6320,12 +7479,21 @@
   DartType getStaticTypeInternal(StaticTypeContext context) =>
       context.typeEnvironment.coreTypes.symbolRawType(context.nonNullable);
 
+  @override
   R accept<R>(ExpressionVisitor<R> v) => v.visitSymbolLiteral(this);
+
+  @override
   R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
       v.visitSymbolLiteral(this, arg);
 
-  visitChildren(Visitor v) {}
-  transformChildren(Transformer v) {}
+  @override
+  void visitChildren(Visitor v) {}
+
+  @override
+  void transformChildren(Transformer v) {}
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {}
 
   @override
   String toString() {
@@ -6344,6 +7512,7 @@
 
   TypeLiteral(this.type);
 
+  @override
   DartType getStaticType(StaticTypeContext context) =>
       getStaticTypeInternal(context);
 
@@ -6351,19 +7520,29 @@
   DartType getStaticTypeInternal(StaticTypeContext context) =>
       context.typeEnvironment.coreTypes.typeRawType(context.nonNullable);
 
+  @override
   R accept<R>(ExpressionVisitor<R> v) => v.visitTypeLiteral(this);
+
+  @override
   R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
       v.visitTypeLiteral(this, arg);
 
-  visitChildren(Visitor v) {
-    type?.accept(v);
+  @override
+  void visitChildren(Visitor v) {
+    type.accept(v);
   }
 
-  transformChildren(Transformer v) {
+  @override
+  void transformChildren(Transformer v) {
     type = v.visitDartType(type);
   }
 
   @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    type = v.visitDartType(type, cannotRemoveSentinel);
+  }
+
+  @override
   String toString() {
     return "TypeLiteral(${toStringInternal()})";
   }
@@ -6379,14 +7558,24 @@
       getStaticTypeInternal(context);
 
   @override
-  DartType getStaticTypeInternal(StaticTypeContext context) => context.thisType;
+  DartType getStaticTypeInternal(StaticTypeContext context) =>
+      context.thisType!;
 
+  @override
   R accept<R>(ExpressionVisitor<R> v) => v.visitThisExpression(this);
+
+  @override
   R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
       v.visitThisExpression(this, arg);
 
-  visitChildren(Visitor v) {}
-  transformChildren(Transformer v) {}
+  @override
+  void visitChildren(Visitor v) {}
+
+  @override
+  void transformChildren(Transformer v) {}
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {}
 
   @override
   String toString() {
@@ -6400,6 +7589,7 @@
 }
 
 class Rethrow extends Expression {
+  @override
   DartType getStaticType(StaticTypeContext context) =>
       getStaticTypeInternal(context);
 
@@ -6409,12 +7599,21 @@
           ? const NeverType.nonNullable()
           : const BottomType();
 
+  @override
   R accept<R>(ExpressionVisitor<R> v) => v.visitRethrow(this);
+
+  @override
   R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
       v.visitRethrow(this, arg);
 
-  visitChildren(Visitor v) {}
-  transformChildren(Transformer v) {}
+  @override
+  void visitChildren(Visitor v) {}
+
+  @override
+  void transformChildren(Transformer v) {}
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {}
 
   @override
   String toString() {
@@ -6431,9 +7630,10 @@
   Expression expression;
 
   Throw(this.expression) {
-    expression?.parent = this;
+    expression.parent = this;
   }
 
+  @override
   DartType getStaticType(StaticTypeContext context) =>
       getStaticTypeInternal(context);
 
@@ -6443,17 +7643,32 @@
           ? const NeverType.nonNullable()
           : const BottomType();
 
+  @override
   R accept<R>(ExpressionVisitor<R> v) => v.visitThrow(this);
+
+  @override
   R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => v.visitThrow(this, arg);
 
-  visitChildren(Visitor v) {
-    expression?.accept(v);
+  @override
+  void visitChildren(Visitor v) {
+    expression.accept(v);
   }
 
-  transformChildren(Transformer v) {
+  @override
+  void transformChildren(Transformer v) {
+    // ignore: unnecessary_null_comparison
     if (expression != null) {
-      expression = expression.accept<TreeNode>(v);
-      expression?.parent = this;
+      expression = v.transform(expression);
+      expression.parent = this;
+    }
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    // ignore: unnecessary_null_comparison
+    if (expression != null) {
+      expression = v.transform(expression);
+      expression.parent = this;
     }
   }
 
@@ -6476,10 +7691,12 @@
 
   ListLiteral(this.expressions,
       {this.typeArgument: const DynamicType(), this.isConst: false}) {
+    // ignore: unnecessary_null_comparison
     assert(typeArgument != null);
     setParents(expressions, this);
   }
 
+  @override
   DartType getStaticType(StaticTypeContext context) =>
       getStaticTypeInternal(context);
 
@@ -6488,18 +7705,29 @@
     return context.typeEnvironment.listType(typeArgument, context.nonNullable);
   }
 
+  @override
   R accept<R>(ExpressionVisitor<R> v) => v.visitListLiteral(this);
+
+  @override
   R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
       v.visitListLiteral(this, arg);
 
-  visitChildren(Visitor v) {
-    typeArgument?.accept(v);
+  @override
+  void visitChildren(Visitor v) {
+    typeArgument.accept(v);
     visitList(expressions, v);
   }
 
-  transformChildren(Transformer v) {
+  @override
+  void transformChildren(Transformer v) {
     typeArgument = v.visitDartType(typeArgument);
-    transformList(expressions, v, this);
+    v.transformList(expressions, this);
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    typeArgument = v.visitDartType(typeArgument, cannotRemoveSentinel);
+    v.transformExpressionList(expressions, this);
   }
 
   @override
@@ -6527,10 +7755,12 @@
 
   SetLiteral(this.expressions,
       {this.typeArgument: const DynamicType(), this.isConst: false}) {
+    // ignore: unnecessary_null_comparison
     assert(typeArgument != null);
     setParents(expressions, this);
   }
 
+  @override
   DartType getStaticType(StaticTypeContext context) =>
       getStaticTypeInternal(context);
 
@@ -6539,18 +7769,29 @@
     return context.typeEnvironment.setType(typeArgument, context.nonNullable);
   }
 
+  @override
   R accept<R>(ExpressionVisitor<R> v) => v.visitSetLiteral(this);
+
+  @override
   R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
       v.visitSetLiteral(this, arg);
 
-  visitChildren(Visitor v) {
-    typeArgument?.accept(v);
+  @override
+  void visitChildren(Visitor v) {
+    typeArgument.accept(v);
     visitList(expressions, v);
   }
 
-  transformChildren(Transformer v) {
+  @override
+  void transformChildren(Transformer v) {
     typeArgument = v.visitDartType(typeArgument);
-    transformList(expressions, v, this);
+    v.transformList(expressions, this);
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    typeArgument = v.visitDartType(typeArgument, cannotRemoveSentinel);
+    v.transformExpressionList(expressions, this);
   }
 
   @override
@@ -6581,11 +7822,14 @@
       {this.keyType: const DynamicType(),
       this.valueType: const DynamicType(),
       this.isConst: false}) {
+    // ignore: unnecessary_null_comparison
     assert(keyType != null);
+    // ignore: unnecessary_null_comparison
     assert(valueType != null);
     setParents(entries, this);
   }
 
+  @override
   DartType getStaticType(StaticTypeContext context) =>
       getStaticTypeInternal(context);
 
@@ -6595,20 +7839,32 @@
         .mapType(keyType, valueType, context.nonNullable);
   }
 
+  @override
   R accept<R>(ExpressionVisitor<R> v) => v.visitMapLiteral(this);
+
+  @override
   R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
       v.visitMapLiteral(this, arg);
 
-  visitChildren(Visitor v) {
-    keyType?.accept(v);
-    valueType?.accept(v);
+  @override
+  void visitChildren(Visitor v) {
+    keyType.accept(v);
+    valueType.accept(v);
     visitList(entries, v);
   }
 
-  transformChildren(Transformer v) {
+  @override
+  void transformChildren(Transformer v) {
     keyType = v.visitDartType(keyType);
     valueType = v.visitDartType(valueType);
-    transformList(entries, v, this);
+    v.transformList(entries, this);
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    keyType = v.visitDartType(keyType, cannotRemoveSentinel);
+    valueType = v.visitDartType(valueType, cannotRemoveSentinel);
+    v.transformMapEntryList(entries, this);
   }
 
   @override
@@ -6641,25 +7897,47 @@
   Expression value;
 
   MapEntry(this.key, this.value) {
-    key?.parent = this;
-    value?.parent = this;
+    key.parent = this;
+    value.parent = this;
   }
 
+  @override
   R accept<R>(TreeVisitor<R> v) => v.visitMapEntry(this);
 
-  visitChildren(Visitor v) {
-    key?.accept(v);
-    value?.accept(v);
+  @override
+  R accept1<R, A>(TreeVisitor1<R, A> v, A arg) => v.visitMapEntry(this, arg);
+
+  @override
+  void visitChildren(Visitor v) {
+    key.accept(v);
+    value.accept(v);
   }
 
-  transformChildren(Transformer v) {
+  @override
+  void transformChildren(Transformer v) {
+    // ignore: unnecessary_null_comparison
     if (key != null) {
-      key = key.accept<TreeNode>(v);
-      key?.parent = this;
+      key = v.transform(key);
+      key.parent = this;
     }
+    // ignore: unnecessary_null_comparison
     if (value != null) {
-      value = value.accept<TreeNode>(v);
-      value?.parent = this;
+      value = v.transform(value);
+      value.parent = this;
+    }
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    // ignore: unnecessary_null_comparison
+    if (key != null) {
+      key = v.transform(key);
+      key.parent = this;
+    }
+    // ignore: unnecessary_null_comparison
+    if (value != null) {
+      value = v.transform(value);
+      value.parent = this;
     }
   }
 
@@ -6686,25 +7964,41 @@
   Expression operand;
 
   AwaitExpression(this.operand) {
-    operand?.parent = this;
+    operand.parent = this;
   }
 
+  @override
   DartType getStaticTypeInternal(StaticTypeContext context) {
     return context.typeEnvironment.flatten(operand.getStaticType(context));
   }
 
+  @override
   R accept<R>(ExpressionVisitor<R> v) => v.visitAwaitExpression(this);
+
+  @override
   R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
       v.visitAwaitExpression(this, arg);
 
-  visitChildren(Visitor v) {
-    operand?.accept(v);
+  @override
+  void visitChildren(Visitor v) {
+    operand.accept(v);
   }
 
-  transformChildren(Transformer v) {
+  @override
+  void transformChildren(Transformer v) {
+    // ignore: unnecessary_null_comparison
     if (operand != null) {
-      operand = operand.accept<TreeNode>(v);
-      operand?.parent = this;
+      operand = v.transform(operand);
+      operand.parent = this;
+    }
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    // ignore: unnecessary_null_comparison
+    if (operand != null) {
+      operand = v.transform(operand);
+      operand.parent = this;
     }
   }
 
@@ -6722,35 +8016,53 @@
 
 /// Common super-interface for [FunctionExpression] and [FunctionDeclaration].
 abstract class LocalFunction implements TreeNode {
-  FunctionNode get function;
+  // TODO(johnniwinther): Make this non-nullable.
+  FunctionNode? get function;
 }
 
 /// Expression of form `(x,y) => ...` or `(x,y) { ... }`
 ///
 /// The arrow-body form `=> e` is desugared into `return e;`.
 class FunctionExpression extends Expression implements LocalFunction {
+  @override
   FunctionNode function;
 
   FunctionExpression(this.function) {
-    function?.parent = this;
+    function.parent = this;
   }
 
+  @override
   DartType getStaticTypeInternal(StaticTypeContext context) {
     return function.computeFunctionType(context.nonNullable);
   }
 
+  @override
   R accept<R>(ExpressionVisitor<R> v) => v.visitFunctionExpression(this);
+
+  @override
   R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
       v.visitFunctionExpression(this, arg);
 
-  visitChildren(Visitor v) {
-    function?.accept(v);
+  @override
+  void visitChildren(Visitor v) {
+    function.accept(v);
   }
 
-  transformChildren(Transformer v) {
+  @override
+  void transformChildren(Transformer v) {
+    // ignore: unnecessary_null_comparison
     if (function != null) {
-      function = function.accept<TreeNode>(v);
-      function?.parent = this;
+      function = v.transform(function);
+      function.parent = this;
+    }
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    // ignore: unnecessary_null_comparison
+    if (function != null) {
+      function = v.transform(function);
+      function.parent = this;
     }
   }
 
@@ -6770,30 +8082,43 @@
   DartType type;
 
   ConstantExpression(this.constant, [this.type = const DynamicType()]) {
+    // ignore: unnecessary_null_comparison
     assert(constant != null);
   }
 
+  @override
   DartType getStaticType(StaticTypeContext context) =>
       getStaticTypeInternal(context);
 
   @override
   DartType getStaticTypeInternal(StaticTypeContext context) => type;
 
+  @override
   R accept<R>(ExpressionVisitor<R> v) => v.visitConstantExpression(this);
+
+  @override
   R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
       v.visitConstantExpression(this, arg);
 
-  visitChildren(Visitor v) {
-    constant?.acceptReference(v);
-    type?.accept(v);
+  @override
+  void visitChildren(Visitor v) {
+    constant.acceptReference(v);
+    type.accept(v);
   }
 
-  transformChildren(Transformer v) {
+  @override
+  void transformChildren(Transformer v) {
     constant = v.visitConstant(constant);
     type = v.visitDartType(type);
   }
 
   @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    constant = v.visitConstant(constant, cannotRemoveSentinel);
+    type = v.visitDartType(type, cannotRemoveSentinel);
+  }
+
+  @override
   String toString() {
     return "ConstantExpression(${toStringInternal()})";
   }
@@ -6810,10 +8135,11 @@
   Expression body;
 
   Let(this.variable, this.body) {
-    variable?.parent = this;
-    body?.parent = this;
+    variable.parent = this;
+    body.parent = this;
   }
 
+  @override
   DartType getStaticType(StaticTypeContext context) =>
       getStaticTypeInternal(context);
 
@@ -6821,22 +8147,43 @@
   DartType getStaticTypeInternal(StaticTypeContext context) =>
       body.getStaticType(context);
 
+  @override
   R accept<R>(ExpressionVisitor<R> v) => v.visitLet(this);
+
+  @override
   R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) => v.visitLet(this, arg);
 
-  visitChildren(Visitor v) {
-    variable?.accept(v);
-    body?.accept(v);
+  @override
+  void visitChildren(Visitor v) {
+    variable.accept(v);
+    body.accept(v);
   }
 
-  transformChildren(Transformer v) {
+  @override
+  void transformChildren(Transformer v) {
+    // ignore: unnecessary_null_comparison
     if (variable != null) {
-      variable = variable.accept<TreeNode>(v);
-      variable?.parent = this;
+      variable = v.transform(variable);
+      variable.parent = this;
     }
+    // ignore: unnecessary_null_comparison
     if (body != null) {
-      body = body.accept<TreeNode>(v);
-      body?.parent = this;
+      body = v.transform(body);
+      body.parent = this;
+    }
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    // ignore: unnecessary_null_comparison
+    if (variable != null) {
+      variable = v.transform(variable);
+      variable.parent = this;
+    }
+    // ignore: unnecessary_null_comparison
+    if (body != null) {
+      body = v.transform(body);
+      body.parent = this;
     }
   }
 
@@ -6859,10 +8206,11 @@
   Expression value;
 
   BlockExpression(this.body, this.value) {
-    body?.parent = this;
-    value?.parent = this;
+    body.parent = this;
+    value.parent = this;
   }
 
+  @override
   DartType getStaticType(StaticTypeContext context) =>
       getStaticTypeInternal(context);
 
@@ -6870,23 +8218,44 @@
   DartType getStaticTypeInternal(StaticTypeContext context) =>
       value.getStaticType(context);
 
+  @override
   R accept<R>(ExpressionVisitor<R> v) => v.visitBlockExpression(this);
+
+  @override
   R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
       v.visitBlockExpression(this, arg);
 
-  visitChildren(Visitor v) {
-    body?.accept(v);
-    value?.accept(v);
+  @override
+  void visitChildren(Visitor v) {
+    body.accept(v);
+    value.accept(v);
   }
 
-  transformChildren(Transformer v) {
+  @override
+  void transformChildren(Transformer v) {
+    // ignore: unnecessary_null_comparison
     if (body != null) {
-      body = body.accept<TreeNode>(v);
-      body?.parent = this;
+      body = v.transform(body);
+      body.parent = this;
     }
+    // ignore: unnecessary_null_comparison
     if (value != null) {
-      value = value.accept<TreeNode>(v);
-      value?.parent = this;
+      value = v.transform(value);
+      value.parent = this;
+    }
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    // ignore: unnecessary_null_comparison
+    if (body != null) {
+      body = v.transform(body);
+      body.parent = this;
+    }
+    // ignore: unnecessary_null_comparison
+    if (value != null) {
+      value = v.transform(value);
+      value.parent = this;
     }
   }
 
@@ -6922,6 +8291,7 @@
 
   LoadLibrary(this.import);
 
+  @override
   DartType getStaticType(StaticTypeContext context) =>
       getStaticTypeInternal(context);
 
@@ -6931,12 +8301,21 @@
         .futureType(const DynamicType(), context.nonNullable);
   }
 
+  @override
   R accept<R>(ExpressionVisitor<R> v) => v.visitLoadLibrary(this);
+
+  @override
   R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
       v.visitLoadLibrary(this, arg);
 
-  visitChildren(Visitor v) {}
-  transformChildren(Transformer v) {}
+  @override
+  void visitChildren(Visitor v) {}
+
+  @override
+  void transformChildren(Transformer v) {}
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {}
 
   @override
   String toString() {
@@ -6945,7 +8324,7 @@
 
   @override
   void toTextInternal(AstPrinter printer) {
-    printer.write(import.name);
+    printer.write(import.name!);
     printer.write('.loadLibrary()');
   }
 }
@@ -6957,6 +8336,7 @@
 
   CheckLibraryIsLoaded(this.import);
 
+  @override
   DartType getStaticType(StaticTypeContext context) =>
       getStaticTypeInternal(context);
 
@@ -6965,12 +8345,21 @@
     return context.typeEnvironment.coreTypes.objectRawType(context.nonNullable);
   }
 
+  @override
   R accept<R>(ExpressionVisitor<R> v) => v.visitCheckLibraryIsLoaded(this);
+
+  @override
   R accept1<R, A>(ExpressionVisitor1<R, A> v, A arg) =>
       v.visitCheckLibraryIsLoaded(this, arg);
 
-  visitChildren(Visitor v) {}
-  transformChildren(Transformer v) {}
+  @override
+  void visitChildren(Visitor v) {}
+
+  @override
+  void transformChildren(Transformer v) {}
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {}
 
   @override
   String toString() {
@@ -6979,7 +8368,7 @@
 
   @override
   void toTextInternal(AstPrinter printer) {
-    printer.write(import.name);
+    printer.write(import.name!);
     printer.write('.checkLibraryIsLoaded()');
   }
 }
@@ -6989,11 +8378,13 @@
 // ------------------------------------------------------------------------
 
 abstract class Statement extends TreeNode {
+  @override
   R accept<R>(StatementVisitor<R> v);
+
+  @override
   R accept1<R, A>(StatementVisitor1<R, A> v, A arg);
 
-  void toTextInternal(AstPrinter printer);
-
+  @override
   String toText(AstTextStrategy strategy) {
     AstPrinter printer = new AstPrinter(strategy);
     printer.writeStatement(this);
@@ -7005,21 +8396,36 @@
   Expression expression;
 
   ExpressionStatement(this.expression) {
-    expression?.parent = this;
+    expression.parent = this;
   }
 
+  @override
   R accept<R>(StatementVisitor<R> v) => v.visitExpressionStatement(this);
+
+  @override
   R accept1<R, A>(StatementVisitor1<R, A> v, A arg) =>
       v.visitExpressionStatement(this, arg);
 
-  visitChildren(Visitor v) {
-    expression?.accept(v);
+  @override
+  void visitChildren(Visitor v) {
+    expression.accept(v);
   }
 
-  transformChildren(Transformer v) {
+  @override
+  void transformChildren(Transformer v) {
+    // ignore: unnecessary_null_comparison
     if (expression != null) {
-      expression = expression.accept<TreeNode>(v);
-      expression?.parent = this;
+      expression = v.transform(expression);
+      expression.parent = this;
+    }
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    // ignore: unnecessary_null_comparison
+    if (expression != null) {
+      expression = v.transform(expression);
+      expression.parent = this;
     }
   }
 
@@ -7045,22 +8451,29 @@
 
   Block(this.statements) {
     // Ensure statements is mutable.
-    assert((statements
-          ..add(null)
-          ..removeLast()) !=
-        null);
+    assert(checkListIsMutable(statements, dummyStatement));
     setParents(statements, this);
   }
 
+  @override
   R accept<R>(StatementVisitor<R> v) => v.visitBlock(this);
+
+  @override
   R accept1<R, A>(StatementVisitor1<R, A> v, A arg) => v.visitBlock(this, arg);
 
-  visitChildren(Visitor v) {
+  @override
+  void visitChildren(Visitor v) {
     visitList(statements, v);
   }
 
-  transformChildren(Transformer v) {
-    transformList(statements, v, this);
+  @override
+  void transformChildren(Transformer v) {
+    v.transformList(statements, this);
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    v.transformStatementList(statements, this);
   }
 
   void addStatement(Statement node) {
@@ -7089,22 +8502,29 @@
 
   AssertBlock(this.statements) {
     // Ensure statements is mutable.
-    assert((statements
-          ..add(null)
-          ..removeLast()) !=
-        null);
+    assert(checkListIsMutable(statements, dummyStatement));
     setParents(statements, this);
   }
 
+  @override
   R accept<R>(StatementVisitor<R> v) => v.visitAssertBlock(this);
+
+  @override
   R accept1<R, A>(StatementVisitor1<R, A> v, A arg) =>
       v.visitAssertBlock(this, arg);
 
-  transformChildren(Transformer v) {
-    transformList(statements, v, this);
+  @override
+  void transformChildren(Transformer v) {
+    v.transformList(statements, this);
   }
 
-  visitChildren(Visitor v) {
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    v.transformStatementList(statements, this);
+  }
+
+  @override
+  void visitChildren(Visitor v) {
     visitList(statements, v);
   }
 
@@ -7126,12 +8546,21 @@
 }
 
 class EmptyStatement extends Statement {
+  @override
   R accept<R>(StatementVisitor<R> v) => v.visitEmptyStatement(this);
+
+  @override
   R accept1<R, A>(StatementVisitor1<R, A> v, A arg) =>
       v.visitEmptyStatement(this, arg);
 
-  visitChildren(Visitor v) {}
-  transformChildren(Transformer v) {}
+  @override
+  void visitChildren(Visitor v) {}
+
+  @override
+  void transformChildren(Transformer v) {}
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {}
 
   @override
   String toString() {
@@ -7146,7 +8575,7 @@
 
 class AssertStatement extends Statement {
   Expression condition;
-  Expression message; // May be null.
+  Expression? message; // May be null.
 
   /// Character offset in the source where the assertion condition begins.
   ///
@@ -7159,27 +8588,48 @@
   int conditionEndOffset;
 
   AssertStatement(this.condition,
-      {this.message, this.conditionStartOffset, this.conditionEndOffset}) {
-    condition?.parent = this;
+      {this.message,
+      required this.conditionStartOffset,
+      required this.conditionEndOffset}) {
+    condition.parent = this;
     message?.parent = this;
   }
 
+  @override
   R accept<R>(StatementVisitor<R> v) => v.visitAssertStatement(this);
+
+  @override
   R accept1<R, A>(StatementVisitor1<R, A> v, A arg) =>
       v.visitAssertStatement(this, arg);
 
-  visitChildren(Visitor v) {
-    condition?.accept(v);
+  @override
+  void visitChildren(Visitor v) {
+    condition.accept(v);
     message?.accept(v);
   }
 
-  transformChildren(Transformer v) {
+  @override
+  void transformChildren(Transformer v) {
+    // ignore: unnecessary_null_comparison
     if (condition != null) {
-      condition = condition.accept<TreeNode>(v);
-      condition?.parent = this;
+      condition = v.transform(condition);
+      condition.parent = this;
     }
     if (message != null) {
-      message = message.accept<TreeNode>(v);
+      message = v.transform(message!);
+      message?.parent = this;
+    }
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    // ignore: unnecessary_null_comparison
+    if (condition != null) {
+      condition = v.transform(condition);
+      condition.parent = this;
+    }
+    if (message != null) {
+      message = v.transformOrRemoveExpression(message!);
       message?.parent = this;
     }
   }
@@ -7195,7 +8645,7 @@
     printer.writeExpression(condition);
     if (message != null) {
       printer.write(', ');
-      printer.writeExpression(message);
+      printer.writeExpression(message!);
     }
     printer.write(');');
   }
@@ -7207,23 +8657,37 @@
 ///
 /// The frontend does not generate labeled statements without uses.
 class LabeledStatement extends Statement {
-  Statement body;
+  // TODO(johnniwinther): Make this non-nullable.
+  Statement? body;
 
   LabeledStatement(this.body) {
     body?.parent = this;
   }
 
+  @override
   R accept<R>(StatementVisitor<R> v) => v.visitLabeledStatement(this);
+
+  @override
   R accept1<R, A>(StatementVisitor1<R, A> v, A arg) =>
       v.visitLabeledStatement(this, arg);
 
-  visitChildren(Visitor v) {
+  @override
+  void visitChildren(Visitor v) {
     body?.accept(v);
   }
 
-  transformChildren(Transformer v) {
+  @override
+  void transformChildren(Transformer v) {
     if (body != null) {
-      body = body.accept<TreeNode>(v);
+      body = v.transform(body!);
+      body?.parent = this;
+    }
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    if (body != null) {
+      body = v.transformOrRemoveStatement(body!);
       body?.parent = this;
     }
   }
@@ -7238,7 +8702,7 @@
     printer.write(printer.getLabelName(this));
     printer.write(':');
     printer.newLine();
-    printer.writeStatement(body);
+    printer.writeStatement(body!);
   }
 }
 
@@ -7267,12 +8731,21 @@
 
   BreakStatement(this.target);
 
+  @override
   R accept<R>(StatementVisitor<R> v) => v.visitBreakStatement(this);
+
+  @override
   R accept1<R, A>(StatementVisitor1<R, A> v, A arg) =>
       v.visitBreakStatement(this, arg);
 
-  visitChildren(Visitor v) {}
-  transformChildren(Transformer v) {}
+  @override
+  void visitChildren(Visitor v) {}
+
+  @override
+  void transformChildren(Transformer v) {}
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {}
 
   @override
   String toString() {
@@ -7292,27 +8765,48 @@
   Statement body;
 
   WhileStatement(this.condition, this.body) {
-    condition?.parent = this;
-    body?.parent = this;
+    condition.parent = this;
+    body.parent = this;
   }
 
+  @override
   R accept<R>(StatementVisitor<R> v) => v.visitWhileStatement(this);
+
+  @override
   R accept1<R, A>(StatementVisitor1<R, A> v, A arg) =>
       v.visitWhileStatement(this, arg);
 
-  visitChildren(Visitor v) {
-    condition?.accept(v);
-    body?.accept(v);
+  @override
+  void visitChildren(Visitor v) {
+    condition.accept(v);
+    body.accept(v);
   }
 
-  transformChildren(Transformer v) {
+  @override
+  void transformChildren(Transformer v) {
+    // ignore: unnecessary_null_comparison
     if (condition != null) {
-      condition = condition.accept<TreeNode>(v);
-      condition?.parent = this;
+      condition = v.transform(condition);
+      condition.parent = this;
     }
+    // ignore: unnecessary_null_comparison
     if (body != null) {
-      body = body.accept<TreeNode>(v);
-      body?.parent = this;
+      body = v.transform(body);
+      body.parent = this;
+    }
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    // ignore: unnecessary_null_comparison
+    if (condition != null) {
+      condition = v.transform(condition);
+      condition.parent = this;
+    }
+    // ignore: unnecessary_null_comparison
+    if (body != null) {
+      body = v.transform(body);
+      body.parent = this;
     }
   }
 
@@ -7335,27 +8829,48 @@
   Expression condition;
 
   DoStatement(this.body, this.condition) {
-    body?.parent = this;
-    condition?.parent = this;
+    body.parent = this;
+    condition.parent = this;
   }
 
+  @override
   R accept<R>(StatementVisitor<R> v) => v.visitDoStatement(this);
+
+  @override
   R accept1<R, A>(StatementVisitor1<R, A> v, A arg) =>
       v.visitDoStatement(this, arg);
 
-  visitChildren(Visitor v) {
-    body?.accept(v);
-    condition?.accept(v);
+  @override
+  void visitChildren(Visitor v) {
+    body.accept(v);
+    condition.accept(v);
   }
 
-  transformChildren(Transformer v) {
+  @override
+  void transformChildren(Transformer v) {
+    // ignore: unnecessary_null_comparison
     if (body != null) {
-      body = body.accept<TreeNode>(v);
-      body?.parent = this;
+      body = v.transform(body);
+      body.parent = this;
     }
+    // ignore: unnecessary_null_comparison
     if (condition != null) {
-      condition = condition.accept<TreeNode>(v);
-      condition?.parent = this;
+      condition = v.transform(condition);
+      condition.parent = this;
+    }
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    // ignore: unnecessary_null_comparison
+    if (body != null) {
+      body = v.transform(body);
+      body.parent = this;
+    }
+    // ignore: unnecessary_null_comparison
+    if (condition != null) {
+      condition = v.transform(condition);
+      condition.parent = this;
     }
   }
 
@@ -7376,7 +8891,7 @@
 
 class ForStatement extends Statement {
   final List<VariableDeclaration> variables; // May be empty, but not null.
-  Expression condition; // May be null.
+  Expression? condition; // May be null.
   final List<Expression> updates; // May be empty, but not null.
   Statement body;
 
@@ -7384,30 +8899,51 @@
     setParents(variables, this);
     condition?.parent = this;
     setParents(updates, this);
-    body?.parent = this;
+    body.parent = this;
   }
 
+  @override
   R accept<R>(StatementVisitor<R> v) => v.visitForStatement(this);
+
+  @override
   R accept1<R, A>(StatementVisitor1<R, A> v, A arg) =>
       v.visitForStatement(this, arg);
 
-  visitChildren(Visitor v) {
+  @override
+  void visitChildren(Visitor v) {
     visitList(variables, v);
     condition?.accept(v);
     visitList(updates, v);
-    body?.accept(v);
+    body.accept(v);
   }
 
-  transformChildren(Transformer v) {
-    transformList(variables, v, this);
+  @override
+  void transformChildren(Transformer v) {
+    v.transformList(variables, this);
     if (condition != null) {
-      condition = condition.accept<TreeNode>(v);
+      condition = v.transform(condition!);
       condition?.parent = this;
     }
-    transformList(updates, v, this);
+    v.transformList(updates, this);
+    // ignore: unnecessary_null_comparison
     if (body != null) {
-      body = body.accept<TreeNode>(v);
-      body?.parent = this;
+      body = v.transform(body);
+      body.parent = this;
+    }
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    v.transformVariableDeclarationList(variables, this);
+    if (condition != null) {
+      condition = v.transformOrRemoveExpression(condition!);
+      condition?.parent = this;
+    }
+    v.transformExpressionList(updates, this);
+    // ignore: unnecessary_null_comparison
+    if (body != null) {
+      body = v.transform(body);
+      body.parent = this;
     }
   }
 
@@ -7428,7 +8964,7 @@
     }
     printer.write('; ');
     if (condition != null) {
-      printer.writeExpression(condition);
+      printer.writeExpression(condition!);
     }
     printer.write('; ');
     printer.writeExpressions(updates);
@@ -7451,33 +8987,60 @@
 
   ForInStatement(this.variable, this.iterable, this.body,
       {this.isAsync: false}) {
-    variable?.parent = this;
-    iterable?.parent = this;
-    body?.parent = this;
+    variable.parent = this;
+    iterable.parent = this;
+    body.parent = this;
   }
 
+  @override
   R accept<R>(StatementVisitor<R> v) => v.visitForInStatement(this);
+
+  @override
   R accept1<R, A>(StatementVisitor1<R, A> v, A arg) =>
       v.visitForInStatement(this, arg);
 
+  @override
   void visitChildren(Visitor v) {
-    variable?.accept(v);
-    iterable?.accept(v);
-    body?.accept(v);
+    variable.accept(v);
+    iterable.accept(v);
+    body.accept(v);
   }
 
+  @override
   void transformChildren(Transformer v) {
+    // ignore: unnecessary_null_comparison
     if (variable != null) {
-      variable = variable.accept<TreeNode>(v);
-      variable?.parent = this;
+      variable = v.transform(variable);
+      variable.parent = this;
     }
+    // ignore: unnecessary_null_comparison
     if (iterable != null) {
-      iterable = iterable.accept<TreeNode>(v);
-      iterable?.parent = this;
+      iterable = v.transform(iterable);
+      iterable.parent = this;
     }
+    // ignore: unnecessary_null_comparison
     if (body != null) {
-      body = body.accept<TreeNode>(v);
-      body?.parent = this;
+      body = v.transform(body);
+      body.parent = this;
+    }
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    // ignore: unnecessary_null_comparison
+    if (variable != null) {
+      variable = v.transform(variable);
+      variable.parent = this;
+    }
+    // ignore: unnecessary_null_comparison
+    if (iterable != null) {
+      iterable = v.transform(iterable);
+      iterable.parent = this;
+    }
+    // ignore: unnecessary_null_comparison
+    if (body != null) {
+      body = v.transform(body);
+      body.parent = this;
     }
   }
 
@@ -7494,10 +9057,11 @@
   /// This is called by `StaticTypeContext.getForInIteratorType` if the iterator
   /// type of this for-in statement is not already cached in [context].
   DartType getIteratorTypeInternal(StaticTypeContext context) {
-    DartType iteratorType;
+    DartType? iteratorType;
     if (isAsync) {
-      InterfaceType streamType = iterable.getStaticTypeAsInstanceOf(
+      InterfaceType? streamType = iterable.getStaticTypeAsInstanceOf(
           context.typeEnvironment.coreTypes.streamClass, context);
+      // ignore: unnecessary_null_comparison
       if (streamType != null) {
         iteratorType = new InterfaceType(
             context.typeEnvironment.coreTypes.streamIteratorClass,
@@ -7507,14 +9071,14 @@
     } else {
       InterfaceType iterableType = iterable.getStaticTypeAsInstanceOf(
           context.typeEnvironment.coreTypes.iterableClass, context);
-      Member member = context.typeEnvironment.hierarchy
+      Member? member = context.typeEnvironment.hierarchy
           .getInterfaceMember(iterableType.classNode, new Name('iterator'));
       if (member != null) {
         iteratorType = Substitution.fromInterfaceType(iterableType)
             .substituteType(member.getterType);
       }
     }
-    return iteratorType ??= const DynamicType();
+    return iteratorType ?? const DynamicType();
   }
 
   /// Returns the type of the element in this for-in statement.
@@ -7535,18 +9099,17 @@
     //  `iterable.iterator.current` if inference is updated accordingly.
     while (iterableType is TypeParameterType) {
       TypeParameterType typeParameterType = iterableType;
-      iterableType =
-          typeParameterType.promotedBound ?? typeParameterType.parameter.bound;
+      iterableType = typeParameterType.bound;
     }
     if (isAsync) {
       List<DartType> typeArguments = context.typeEnvironment
-          .getTypeArgumentsAsInstanceOf(
-              iterableType, context.typeEnvironment.coreTypes.streamClass);
+          .getTypeArgumentsAsInstanceOf(iterableType as InterfaceType,
+              context.typeEnvironment.coreTypes.streamClass)!;
       return typeArguments.single;
     } else {
       List<DartType> typeArguments = context.typeEnvironment
-          .getTypeArgumentsAsInstanceOf(
-              iterableType, context.typeEnvironment.coreTypes.iterableClass);
+          .getTypeArgumentsAsInstanceOf(iterableType as InterfaceType,
+              context.typeEnvironment.coreTypes.iterableClass)!;
       return typeArguments.single;
     }
   }
@@ -7577,25 +9140,41 @@
   final List<SwitchCase> cases;
 
   SwitchStatement(this.expression, this.cases) {
-    expression?.parent = this;
+    expression.parent = this;
     setParents(cases, this);
   }
 
+  @override
   R accept<R>(StatementVisitor<R> v) => v.visitSwitchStatement(this);
+
+  @override
   R accept1<R, A>(StatementVisitor1<R, A> v, A arg) =>
       v.visitSwitchStatement(this, arg);
 
-  visitChildren(Visitor v) {
-    expression?.accept(v);
+  @override
+  void visitChildren(Visitor v) {
+    expression.accept(v);
     visitList(cases, v);
   }
 
-  transformChildren(Transformer v) {
+  @override
+  void transformChildren(Transformer v) {
+    // ignore: unnecessary_null_comparison
     if (expression != null) {
-      expression = expression.accept<TreeNode>(v);
-      expression?.parent = this;
+      expression = v.transform(expression);
+      expression.parent = this;
     }
-    transformList(cases, v, this);
+    v.transformList(cases, this);
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    // ignore: unnecessary_null_comparison
+    if (expression != null) {
+      expression = v.transform(expression);
+      expression.parent = this;
+    }
+    v.transformSwitchCaseList(cases, this);
   }
 
   @override
@@ -7625,7 +9204,8 @@
 class SwitchCase extends TreeNode {
   final List<Expression> expressions;
   final List<int> expressionOffsets;
-  Statement body;
+  // TODO(johnniwinther): Make this non-nullable.
+  Statement? body;
   bool isDefault;
 
   SwitchCase(this.expressions, this.expressionOffsets, this.body,
@@ -7647,17 +9227,32 @@
         body = null,
         isDefault = false;
 
+  @override
   R accept<R>(TreeVisitor<R> v) => v.visitSwitchCase(this);
 
-  visitChildren(Visitor v) {
+  @override
+  R accept1<R, A>(TreeVisitor1<R, A> v, A arg) => v.visitSwitchCase(this, arg);
+
+  @override
+  void visitChildren(Visitor v) {
     visitList(expressions, v);
     body?.accept(v);
   }
 
-  transformChildren(Transformer v) {
-    transformList(expressions, v, this);
+  @override
+  void transformChildren(Transformer v) {
+    v.transformList(expressions, this);
     if (body != null) {
-      body = body.accept<TreeNode>(v);
+      body = v.transform(body!);
+      body?.parent = this;
+    }
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    v.transformExpressionList(expressions, this);
+    if (body != null) {
+      body = v.transformOrRemoveStatement(body!);
       body?.parent = this;
     }
   }
@@ -7667,12 +9262,14 @@
     return "SwitchCase(${toStringInternal()})";
   }
 
+  @override
   String toText(AstTextStrategy strategy) {
     AstPrinter printer = new AstPrinter(strategy);
     toTextInternal(printer);
     return printer.getText();
   }
 
+  @override
   void toTextInternal(AstPrinter printer) {
     for (int index = 0; index < expressions.length; index++) {
       if (index > 0) {
@@ -7689,7 +9286,7 @@
       printer.write('default:');
     }
     printer.incIndentation();
-    Statement block = body;
+    Statement? block = body;
     if (block is Block) {
       for (Statement statement in block.statements) {
         printer.newLine();
@@ -7697,7 +9294,7 @@
       }
     } else {
       printer.write(' ');
-      printer.writeStatement(body);
+      printer.writeStatement(body!);
     }
     printer.decIndentation();
   }
@@ -7709,12 +9306,21 @@
 
   ContinueSwitchStatement(this.target);
 
+  @override
   R accept<R>(StatementVisitor<R> v) => v.visitContinueSwitchStatement(this);
+
+  @override
   R accept1<R, A>(StatementVisitor1<R, A> v, A arg) =>
       v.visitContinueSwitchStatement(this, arg);
 
-  visitChildren(Visitor v) {}
-  transformChildren(Transformer v) {}
+  @override
+  void visitChildren(Visitor v) {}
+
+  @override
+  void transformChildren(Transformer v) {}
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {}
 
   @override
   String toString() {
@@ -7732,35 +9338,60 @@
 class IfStatement extends Statement {
   Expression condition;
   Statement then;
-  Statement otherwise;
+  Statement? otherwise;
 
   IfStatement(this.condition, this.then, this.otherwise) {
-    condition?.parent = this;
-    then?.parent = this;
+    condition.parent = this;
+    then.parent = this;
     otherwise?.parent = this;
   }
 
+  @override
   R accept<R>(StatementVisitor<R> v) => v.visitIfStatement(this);
+
+  @override
   R accept1<R, A>(StatementVisitor1<R, A> v, A arg) =>
       v.visitIfStatement(this, arg);
 
-  visitChildren(Visitor v) {
-    condition?.accept(v);
-    then?.accept(v);
+  @override
+  void visitChildren(Visitor v) {
+    condition.accept(v);
+    then.accept(v);
     otherwise?.accept(v);
   }
 
-  transformChildren(Transformer v) {
+  @override
+  void transformChildren(Transformer v) {
+    // ignore: unnecessary_null_comparison
     if (condition != null) {
-      condition = condition.accept<TreeNode>(v);
-      condition?.parent = this;
+      condition = v.transform(condition);
+      condition.parent = this;
     }
+    // ignore: unnecessary_null_comparison
     if (then != null) {
-      then = then.accept<TreeNode>(v);
-      then?.parent = this;
+      then = v.transform(then);
+      then.parent = this;
     }
     if (otherwise != null) {
-      otherwise = otherwise.accept<TreeNode>(v);
+      otherwise = v.transform(otherwise!);
+      otherwise?.parent = this;
+    }
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    // ignore: unnecessary_null_comparison
+    if (condition != null) {
+      condition = v.transform(condition);
+      condition.parent = this;
+    }
+    // ignore: unnecessary_null_comparison
+    if (then != null) {
+      then = v.transform(then);
+      then.parent = this;
+    }
+    if (otherwise != null) {
+      otherwise = v.transformOrRemoveStatement(otherwise!);
       otherwise?.parent = this;
     }
   }
@@ -7778,29 +9409,42 @@
     printer.writeStatement(then);
     if (otherwise != null) {
       printer.write(' else ');
-      printer.writeStatement(otherwise);
+      printer.writeStatement(otherwise!);
     }
   }
 }
 
 class ReturnStatement extends Statement {
-  Expression expression; // May be null.
+  Expression? expression; // May be null.
 
   ReturnStatement([this.expression]) {
     expression?.parent = this;
   }
 
+  @override
   R accept<R>(StatementVisitor<R> v) => v.visitReturnStatement(this);
+
+  @override
   R accept1<R, A>(StatementVisitor1<R, A> v, A arg) =>
       v.visitReturnStatement(this, arg);
 
-  visitChildren(Visitor v) {
+  @override
+  void visitChildren(Visitor v) {
     expression?.accept(v);
   }
 
-  transformChildren(Transformer v) {
+  @override
+  void transformChildren(Transformer v) {
     if (expression != null) {
-      expression = expression.accept<TreeNode>(v);
+      expression = v.transform(expression!);
+      expression?.parent = this;
+    }
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    if (expression != null) {
+      expression = v.transformOrRemoveExpression(expression!);
       expression?.parent = this;
     }
   }
@@ -7815,7 +9459,7 @@
     printer.write('return');
     if (expression != null) {
       printer.write(' ');
-      printer.writeExpression(expression);
+      printer.writeExpression(expression!);
     }
     printer.write(';');
   }
@@ -7827,25 +9471,41 @@
   bool isSynthetic;
 
   TryCatch(this.body, this.catches, {this.isSynthetic: false}) {
-    body?.parent = this;
+    body.parent = this;
     setParents(catches, this);
   }
 
+  @override
   R accept<R>(StatementVisitor<R> v) => v.visitTryCatch(this);
+
+  @override
   R accept1<R, A>(StatementVisitor1<R, A> v, A arg) =>
       v.visitTryCatch(this, arg);
 
-  visitChildren(Visitor v) {
-    body?.accept(v);
+  @override
+  void visitChildren(Visitor v) {
+    body.accept(v);
     visitList(catches, v);
   }
 
-  transformChildren(Transformer v) {
+  @override
+  void transformChildren(Transformer v) {
+    // ignore: unnecessary_null_comparison
     if (body != null) {
-      body = body.accept<TreeNode>(v);
-      body?.parent = this;
+      body = v.transform(body);
+      body.parent = this;
     }
-    transformList(catches, v, this);
+    v.transformList(catches, this);
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    // ignore: unnecessary_null_comparison
+    if (body != null) {
+      body = v.transform(body);
+      body.parent = this;
+    }
+    v.transformCatchList(catches, this);
   }
 
   @override
@@ -7866,40 +9526,66 @@
 
 class Catch extends TreeNode {
   DartType guard; // Not null, defaults to dynamic.
-  VariableDeclaration exception; // May be null.
-  VariableDeclaration stackTrace; // May be null.
+  VariableDeclaration? exception;
+  VariableDeclaration? stackTrace;
   Statement body;
 
   Catch(this.exception, this.body,
       {this.guard: const DynamicType(), this.stackTrace}) {
+    // ignore: unnecessary_null_comparison
     assert(guard != null);
     exception?.parent = this;
     stackTrace?.parent = this;
-    body?.parent = this;
+    body.parent = this;
   }
 
+  @override
   R accept<R>(TreeVisitor<R> v) => v.visitCatch(this);
 
-  visitChildren(Visitor v) {
-    guard?.accept(v);
+  @override
+  R accept1<R, A>(TreeVisitor1<R, A> v, A arg) => v.visitCatch(this, arg);
+
+  @override
+  void visitChildren(Visitor v) {
+    guard.accept(v);
     exception?.accept(v);
     stackTrace?.accept(v);
-    body?.accept(v);
+    body.accept(v);
   }
 
-  transformChildren(Transformer v) {
+  @override
+  void transformChildren(Transformer v) {
     guard = v.visitDartType(guard);
     if (exception != null) {
-      exception = exception.accept<TreeNode>(v);
+      exception = v.transform(exception!);
       exception?.parent = this;
     }
     if (stackTrace != null) {
-      stackTrace = stackTrace.accept<TreeNode>(v);
+      stackTrace = v.transform(stackTrace!);
       stackTrace?.parent = this;
     }
+    // ignore: unnecessary_null_comparison
     if (body != null) {
-      body = body.accept<TreeNode>(v);
-      body?.parent = this;
+      body = v.transform(body);
+      body.parent = this;
+    }
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    guard = v.visitDartType(guard, cannotRemoveSentinel);
+    if (exception != null) {
+      exception = v.transformOrRemoveVariableDeclaration(exception!);
+      exception?.parent = this;
+    }
+    if (stackTrace != null) {
+      stackTrace = v.transformOrRemoveVariableDeclaration(stackTrace!);
+      stackTrace?.parent = this;
+    }
+    // ignore: unnecessary_null_comparison
+    if (body != null) {
+      body = v.transform(body);
+      body.parent = this;
     }
   }
 
@@ -7908,12 +9594,14 @@
     return "Catch(${toStringInternal()})";
   }
 
+  @override
   String toText(AstTextStrategy strategy) {
     AstPrinter printer = new AstPrinter(strategy);
     toTextInternal(printer);
     return printer.getText();
   }
 
+  @override
   void toTextInternal(AstPrinter printer) {
     bool isImplicitType(DartType type) {
       if (type is DynamicType) {
@@ -7922,9 +9610,9 @@
       if (type is InterfaceType &&
           type.className.node != null &&
           type.classNode.name == 'Object') {
-        Uri uri = type.classNode.enclosingLibrary?.importUri;
-        return uri?.scheme == 'dart' &&
-            uri?.path == 'core' &&
+        Uri uri = type.classNode.enclosingLibrary.importUri;
+        return uri.scheme == 'dart' &&
+            uri.path == 'core' &&
             type.nullability == Nullability.nonNullable;
       }
       return false;
@@ -7937,11 +9625,11 @@
         printer.write(' ');
       }
       printer.write('catch (');
-      printer.writeVariableDeclaration(exception,
+      printer.writeVariableDeclaration(exception!,
           includeModifiersAndType: false);
       if (stackTrace != null) {
         printer.write(', ');
-        printer.writeVariableDeclaration(stackTrace,
+        printer.writeVariableDeclaration(stackTrace!,
             includeModifiersAndType: false);
       }
       printer.write(') ');
@@ -7959,27 +9647,48 @@
   Statement finalizer;
 
   TryFinally(this.body, this.finalizer) {
-    body?.parent = this;
-    finalizer?.parent = this;
+    body.parent = this;
+    finalizer.parent = this;
   }
 
+  @override
   R accept<R>(StatementVisitor<R> v) => v.visitTryFinally(this);
+
+  @override
   R accept1<R, A>(StatementVisitor1<R, A> v, A arg) =>
       v.visitTryFinally(this, arg);
 
-  visitChildren(Visitor v) {
-    body?.accept(v);
-    finalizer?.accept(v);
+  @override
+  void visitChildren(Visitor v) {
+    body.accept(v);
+    finalizer.accept(v);
   }
 
-  transformChildren(Transformer v) {
+  @override
+  void transformChildren(Transformer v) {
+    // ignore: unnecessary_null_comparison
     if (body != null) {
-      body = body.accept<TreeNode>(v);
-      body?.parent = this;
+      body = v.transform(body);
+      body.parent = this;
     }
+    // ignore: unnecessary_null_comparison
     if (finalizer != null) {
-      finalizer = finalizer.accept<TreeNode>(v);
-      finalizer?.parent = this;
+      finalizer = v.transform(finalizer);
+      finalizer.parent = this;
+    }
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    // ignore: unnecessary_null_comparison
+    if (body != null) {
+      body = v.transform(body);
+      body.parent = this;
+    }
+    // ignore: unnecessary_null_comparison
+    if (finalizer != null) {
+      finalizer = v.transform(finalizer);
+      finalizer.parent = this;
     }
   }
 
@@ -8009,7 +9718,7 @@
 
   YieldStatement(this.expression,
       {bool isYieldStar: false, bool isNative: false}) {
-    expression?.parent = this;
+    expression.parent = this;
     this.isYieldStar = isYieldStar;
     this.isNative = isNative;
   }
@@ -8028,18 +9737,33 @@
     flags = value ? (flags | FlagNative) : (flags & ~FlagNative);
   }
 
+  @override
   R accept<R>(StatementVisitor<R> v) => v.visitYieldStatement(this);
+
+  @override
   R accept1<R, A>(StatementVisitor1<R, A> v, A arg) =>
       v.visitYieldStatement(this, arg);
 
-  visitChildren(Visitor v) {
-    expression?.accept(v);
+  @override
+  void visitChildren(Visitor v) {
+    expression.accept(v);
   }
 
-  transformChildren(Transformer v) {
+  @override
+  void transformChildren(Transformer v) {
+    // ignore: unnecessary_null_comparison
     if (expression != null) {
-      expression = expression.accept<TreeNode>(v);
-      expression?.parent = this;
+      expression = v.transform(expression);
+      expression.parent = this;
+    }
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    // ignore: unnecessary_null_comparison
+    if (expression != null) {
+      expression = v.transform(expression);
+      expression.parent = this;
     }
   }
 
@@ -8087,7 +9811,7 @@
   ///
   /// In all other cases, the name is cosmetic, may be empty or null,
   /// and is not necessarily unique.
-  String name;
+  String? name;
   int flags = 0;
   DartType type; // Not null, defaults to dynamic.
 
@@ -8098,7 +9822,7 @@
   /// For parameters, this is the default value.
   ///
   /// Should be null in other cases.
-  Expression initializer; // May be null.
+  Expression? initializer; // May be null.
 
   VariableDeclaration(this.name,
       {this.initializer,
@@ -8111,6 +9835,7 @@
       bool isLate: false,
       bool isRequired: false,
       bool isLowered: false}) {
+    // ignore: unnecessary_null_comparison
     assert(type != null);
     initializer?.parent = this;
     if (flags != -1) {
@@ -8135,6 +9860,7 @@
       bool isRequired: false,
       bool isLowered: false,
       this.type: const DynamicType()}) {
+    // ignore: unnecessary_null_comparison
     assert(type != null);
     initializer?.parent = this;
     this.isFinal = isFinal;
@@ -8254,27 +9980,43 @@
     annotations.add(annotation..parent = this);
   }
 
+  @override
   R accept<R>(StatementVisitor<R> v) => v.visitVariableDeclaration(this);
+
+  @override
   R accept1<R, A>(StatementVisitor1<R, A> v, A arg) =>
       v.visitVariableDeclaration(this, arg);
 
-  visitChildren(Visitor v) {
+  @override
+  void visitChildren(Visitor v) {
     visitList(annotations, v);
-    type?.accept(v);
+    type.accept(v);
     initializer?.accept(v);
   }
 
-  transformChildren(Transformer v) {
-    transformList(annotations, v, this);
+  @override
+  void transformChildren(Transformer v) {
+    v.transformList(annotations, this);
     type = v.visitDartType(type);
     if (initializer != null) {
-      initializer = initializer.accept<TreeNode>(v);
+      initializer = v.transform(initializer!);
+      initializer?.parent = this;
+    }
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    v.transformExpressionList(annotations, this);
+    type = v.visitDartType(type, cannotRemoveSentinel);
+    if (initializer != null) {
+      initializer = v.transformOrRemoveExpression(initializer!);
       initializer?.parent = this;
     }
   }
 
   /// Returns a possibly synthesized name for this variable, consistent with
   /// the names used across all [toString] calls.
+  @override
   String toString() {
     return "VariableDeclaration(${toStringInternal()})";
   }
@@ -8298,29 +10040,50 @@
 /// The body of the function may use [variable] as its self-reference.
 class FunctionDeclaration extends Statement implements LocalFunction {
   VariableDeclaration variable; // Is final and has no initializer.
-  FunctionNode function;
+
+  @override
+  FunctionNode? function;
 
   FunctionDeclaration(this.variable, this.function) {
-    variable?.parent = this;
+    variable.parent = this;
     function?.parent = this;
   }
 
+  @override
   R accept<R>(StatementVisitor<R> v) => v.visitFunctionDeclaration(this);
+
+  @override
   R accept1<R, A>(StatementVisitor1<R, A> v, A arg) =>
       v.visitFunctionDeclaration(this, arg);
 
-  visitChildren(Visitor v) {
-    variable?.accept(v);
+  @override
+  void visitChildren(Visitor v) {
+    variable.accept(v);
     function?.accept(v);
   }
 
-  transformChildren(Transformer v) {
+  @override
+  void transformChildren(Transformer v) {
+    // ignore: unnecessary_null_comparison
     if (variable != null) {
-      variable = variable.accept<TreeNode>(v);
-      variable?.parent = this;
+      variable = v.transform(variable);
+      variable.parent = this;
     }
     if (function != null) {
-      function = function.accept<TreeNode>(v);
+      function = v.transform(function!);
+      function?.parent = this;
+    }
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    // ignore: unnecessary_null_comparison
+    if (variable != null) {
+      variable = v.transform(variable);
+      variable.parent = this;
+    }
+    if (function != null) {
+      function = v.transformOrRemove(function!, dummyFunctionNode);
       function?.parent = this;
     }
   }
@@ -8332,9 +10095,11 @@
 
   @override
   void toTextInternal(AstPrinter printer) {
-    printer.writeFunctionNode(function, printer.getVariableName(variable));
-    if (function.body is ReturnStatement) {
-      printer.write(';');
+    if (function != null) {
+      printer.writeFunctionNode(function!, printer.getVariableName(variable));
+      if (function!.body is ReturnStatement) {
+        printer.write(';');
+      }
     }
   }
 }
@@ -8354,23 +10119,25 @@
 /// The [toString] method returns a human-readable string that includes the
 /// library name for private names; uniqueness is not guaranteed.
 abstract class Name extends Node {
+  @override
   final int hashCode;
+
   final String text;
-  Reference get libraryName;
-  Library get library;
+  Reference? get libraryName;
+  Library? get library;
   bool get isPrivate;
 
   Name._internal(this.hashCode, this.text);
 
-  factory Name(String text, [Library library]) =>
+  factory Name(String text, [Library? library]) =>
       new Name.byReference(text, library?.reference);
 
-  factory Name.byReference(String text, Reference libraryName) {
+  factory Name.byReference(String text, Reference? libraryName) {
     /// Use separate subclasses for the public and private case to save memory
     /// for public names.
     if (text.startsWith('_')) {
       assert(libraryName != null);
-      return new _PrivateName(text, libraryName);
+      return new _PrivateName(text, libraryName!);
     } else {
       return new _PublicName(text);
     }
@@ -8380,13 +10147,16 @@
   // use [text].
   String get name => text;
 
+  @override
   bool operator ==(other) {
     return other is Name && text == other.text && library == other.library;
   }
 
+  @override
   R accept<R>(Visitor<R> v) => v.visitName(this);
 
-  visitChildren(Visitor v) {
+  @override
+  void visitChildren(Visitor v) {
     // DESIGN TODO: Should we visit the library as a library reference?
   }
 
@@ -8394,6 +10164,7 @@
   ///
   /// Note that this adds some nodes to a static map to ensure consistent
   /// naming, but that it thus also leaks memory.
+  @override
   String leakingDebugToString() => astToText.debugNodeToString(this);
 
   @override
@@ -8409,17 +10180,25 @@
 }
 
 class _PrivateName extends Name {
+  @override
   final Reference libraryName;
+
+  @override
   bool get isPrivate => true;
 
   _PrivateName(String text, Reference libraryName)
       : this.libraryName = libraryName,
         super._internal(_computeHashCode(text, libraryName), text);
 
+  @override
   String toString() => toStringInternal();
 
-  String toStringInternal() => library != null ? '$library::$text' : text;
+  @override
+  String toStringInternal() =>
+      // ignore: unnecessary_null_comparison
+      library != null ? '$library::$text' : text;
 
+  @override
   Library get library => libraryName.asLibrary;
 
   static int _computeHashCode(String name, Reference libraryName) {
@@ -8431,12 +10210,18 @@
 }
 
 class _PublicName extends Name {
-  Reference get libraryName => null;
-  Library get library => null;
+  @override
+  Reference? get libraryName => null;
+
+  @override
+  Library? get library => null;
+
+  @override
   bool get isPrivate => false;
 
   _PublicName(String text) : super._internal(text.hashCode, text);
 
+  @override
   String toString() => toStringInternal();
 }
 
@@ -8554,17 +10339,19 @@
         nullability == Nullability.undetermined;
   }
 
-  bool equals(Object other, Assumptions assumptions);
+  bool equals(Object other, Assumptions? assumptions);
 
   /// Returns a textual representation of the this type.
   ///
   /// If [verbose] is `true`, qualified names will include the library name/uri.
+  @override
   String toText(AstTextStrategy strategy) {
     AstPrinter printer = new AstPrinter(strategy);
     printer.writeType(this);
     return printer.getText();
   }
 
+  @override
   void toTextInternal(AstPrinter printer);
 }
 
@@ -8592,7 +10379,7 @@
   bool operator ==(Object other) => equals(other, null);
 
   @override
-  bool equals(Object other, Assumptions assumptions) => other is InvalidType;
+  bool equals(Object other, Assumptions? assumptions) => other is InvalidType;
 
   @override
   Nullability get declaredNullability {
@@ -8640,7 +10427,7 @@
   bool operator ==(Object other) => equals(other, null);
 
   @override
-  bool equals(Object other, Assumptions assumptions) => other is DynamicType;
+  bool equals(Object other, Assumptions? assumptions) => other is DynamicType;
 
   @override
   Nullability get declaredNullability => Nullability.nullable;
@@ -8682,7 +10469,7 @@
   bool operator ==(Object other) => equals(other, null);
 
   @override
-  bool equals(Object other, Assumptions assumptions) => other is VoidType;
+  bool equals(Object other, Assumptions? assumptions) => other is VoidType;
 
   @override
   Nullability get declaredNullability => Nullability.nullable;
@@ -8729,7 +10516,6 @@
       case Nullability.undetermined:
         return const NeverType.undetermined();
     }
-    throw new StateError("Unhandled nullability value '${nullability}'.");
   }
 
   @override
@@ -8754,7 +10540,7 @@
   bool operator ==(Object other) => equals(other, null);
 
   @override
-  bool equals(Object other, Assumptions assumptions) =>
+  bool equals(Object other, Assumptions? assumptions) =>
       other is NeverType && nullability == other.nullability;
 
   @override
@@ -8796,7 +10582,7 @@
   bool operator ==(Object other) => equals(other, null);
 
   @override
-  bool equals(Object other, Assumptions assumptions) => other is BottomType;
+  bool equals(Object other, Assumptions? assumptions) => other is BottomType;
 
   @override
   Nullability get declaredNullability => Nullability.nonNullable;
@@ -8839,7 +10625,7 @@
   bool operator ==(Object other) => equals(other, null);
 
   @override
-  bool equals(Object other, Assumptions assumptions) => other is NullType;
+  bool equals(Object other, Assumptions? assumptions) => other is NullType;
 
   @override
   Nullability get declaredNullability => Nullability.nullable;
@@ -8872,12 +10658,15 @@
   /// The [typeArguments] list must not be modified after this call. If the
   /// list is omitted, 'dynamic' type arguments are filled in.
   InterfaceType(Class classNode, Nullability declaredNullability,
-      [List<DartType> typeArguments])
-      : this.byReference(getClassReference(classNode), declaredNullability,
+      [List<DartType>? typeArguments])
+      : this.byReference(
+            getNonNullableClassReference(classNode),
+            declaredNullability,
             typeArguments ?? _defaultTypeArguments(classNode));
 
   InterfaceType.byReference(
       this.className, this.declaredNullability, this.typeArguments)
+      // ignore: unnecessary_null_comparison
       : assert(declaredNullability != null);
 
   Class get classNode => className.asClass;
@@ -8912,7 +10701,7 @@
   bool operator ==(Object other) => equals(other, null);
 
   @override
-  bool equals(Object other, Assumptions assumptions) {
+  bool equals(Object other, Assumptions? assumptions) {
     if (identical(this, other)) return true;
     if (other is InterfaceType) {
       if (nullability != other.nullability) return false;
@@ -8972,24 +10761,26 @@
   final Nullability declaredNullability;
 
   /// The [Typedef] this function type is created for.
-  final TypedefType typedefType;
+  final TypedefType? typedefType;
 
   final DartType returnType;
-  int _hashCode;
+
+  @override
+  late final int hashCode = _computeHashCode();
 
   FunctionType(List<DartType> positionalParameters, this.returnType,
       this.declaredNullability,
       {this.namedParameters: const <NamedType>[],
       this.typeParameters: const <TypeParameter>[],
-      int requiredParameterCount,
+      int? requiredParameterCount,
       this.typedefType})
       : this.positionalParameters = positionalParameters,
         this.requiredParameterCount =
             requiredParameterCount ?? positionalParameters.length;
 
-  Reference get typedefReference => typedefType?.typedefReference;
+  Reference? get typedefReference => typedefType?.typedefReference;
 
-  Typedef get typedef => typedefReference?.asTypedef;
+  Typedef? get typedef => typedefReference?.asTypedef;
 
   @override
   Nullability get nullability => declaredNullability;
@@ -9014,7 +10805,7 @@
   bool operator ==(Object other) => equals(other, null);
 
   @override
-  bool equals(Object other, Assumptions assumptions) {
+  bool equals(Object other, Assumptions? assumptions) {
     if (identical(this, other)) {
       return true;
     } else if (other is FunctionType) {
@@ -9033,8 +10824,8 @@
         }
         for (int index = 0; index < typeParameters.length; index++) {
           if (!typeParameters[index]
-              .bound
-              .equals(other.typeParameters[index].bound, assumptions)) {
+              .bound!
+              .equals(other.typeParameters[index].bound!, assumptions)) {
             return false;
           }
         }
@@ -9057,8 +10848,8 @@
       }
       if (typeParameters.isNotEmpty) {
         for (int index = 0; index < typeParameters.length; index++) {
-          assumptions.forget(
-              typeParameters[index], other.typeParameters[index]);
+          assumptions!
+              .forget(typeParameters[index], other.typeParameters[index]);
         }
       }
       return true;
@@ -9083,7 +10874,7 @@
   /// Looks up the type of the named parameter with the given name.
   ///
   /// Returns `null` if there is no named parameter with the given name.
-  DartType getNamedParameter(String name) {
+  DartType? getNamedParameter(String name) {
     int lower = 0;
     int upper = namedParameters.length - 1;
     while (lower <= upper) {
@@ -9101,9 +10892,6 @@
     return null;
   }
 
-  @override
-  int get hashCode => _hashCode ??= _computeHashCode();
-
   int _computeHashCode() {
     int hash = 1237;
     hash = 0x3fffffff & (hash * 31 + requiredParameterCount);
@@ -9186,7 +10974,7 @@
   final List<DartType> typeArguments;
 
   TypedefType(Typedef typedefNode, Nullability nullability,
-      [List<DartType> typeArguments])
+      [List<DartType>? typeArguments])
       : this.byReference(typedefNode.reference, nullability,
             typeArguments ?? const <DartType>[]);
 
@@ -9216,7 +11004,7 @@
   @override
   DartType get unaliasOnce {
     DartType result =
-        Substitution.fromTypedefType(this).substituteType(typedefNode.type);
+        Substitution.fromTypedefType(this).substituteType(typedefNode.type!);
     return result.withDeclaredNullability(
         combineNullabilitiesForSubstitution(result.nullability, nullability));
   }
@@ -9230,7 +11018,7 @@
   bool operator ==(Object other) => equals(other, null);
 
   @override
-  bool equals(Object other, Assumptions assumptions) {
+  bool equals(Object other, Assumptions? assumptions) {
     if (identical(this, other)) {
       return true;
     } else if (other is TypedefType) {
@@ -9311,7 +11099,7 @@
   bool operator ==(Object other) => equals(other, null);
 
   @override
-  bool equals(Object other, Assumptions assumptions) {
+  bool equals(Object other, Assumptions? assumptions) {
     if (identical(this, other)) return true;
     if (other is FutureOrType) {
       if (declaredNullability != other.declaredNullability) return false;
@@ -9369,7 +11157,7 @@
   @override
   bool operator ==(Object other) => equals(other, null);
 
-  bool equals(Object other, Assumptions assumptions) {
+  bool equals(Object other, Assumptions? assumptions) {
     return other is NamedType &&
         name == other.name &&
         isRequired == other.isRequired &&
@@ -9435,10 +11223,11 @@
   ///
   /// 'null' indicates that the type parameter's bound has not been promoted and
   /// is therefore the same as the bound of [parameter].
-  DartType promotedBound;
+  DartType? promotedBound;
 
   TypeParameterType.internal(
-      this.parameter, this.declaredNullability, this.promotedBound) {
+      this.parameter, this.declaredNullability, DartType? promotedBound)
+      : this.promotedBound = promotedBound {
     assert(
         promotedBound == null ||
             (declaredNullability == Nullability.nonNullable &&
@@ -9512,7 +11301,7 @@
   }
 
   TypeParameterType(TypeParameter parameter, Nullability declaredNullability,
-      [DartType promotedBound])
+      [DartType? promotedBound])
       : this.internal(parameter, declaredNullability, promotedBound);
 
   /// Creates an intersection type between a type parameter and [promotedBound].
@@ -9539,11 +11328,10 @@
   /// `Nullability.undetermined` will be used, depending on the nullability of
   /// the bound of [parameter].
   TypeParameterType.withDefaultNullabilityForLibrary(
-      this.parameter, Library library) {
-    declaredNullability = library.isNonNullableByDefault
-        ? computeNullabilityFromBound(parameter)
-        : Nullability.legacy;
-  }
+      this.parameter, Library library)
+      : declaredNullability = library.isNonNullableByDefault
+            ? computeNullabilityFromBound(parameter)
+            : Nullability.legacy;
 
   @override
   R accept<R>(DartTypeVisitor<R> v) => v.visitTypeParameterType(this);
@@ -9559,7 +11347,7 @@
   bool operator ==(Object other) => equals(other, null);
 
   @override
-  bool equals(Object other, Assumptions assumptions) {
+  bool equals(Object other, Assumptions? assumptions) {
     if (identical(this, other)) {
       return true;
     } else if (other is TypeParameterType) {
@@ -9579,7 +11367,7 @@
       }
       if (promotedBound != null) {
         if (other.promotedBound == null) return false;
-        if (!promotedBound.equals(other.promotedBound, assumptions)) {
+        if (!promotedBound!.equals(other.promotedBound!, assumptions)) {
           return false;
         }
       } else if (other.promotedBound != null) {
@@ -9604,7 +11392,7 @@
   }
 
   /// Returns the bound of the type parameter, accounting for promotions.
-  DartType get bound => promotedBound ?? parameter.bound;
+  DartType get bound => (promotedBound ?? parameter.bound)!;
 
   /// Nullability of the type, calculated from its parts.
   ///
@@ -9627,9 +11415,7 @@
   ///     }
   @override
   Nullability get nullability {
-    return getNullability(
-        declaredNullability ?? computeNullabilityFromBound(parameter),
-        promotedBound);
+    return getNullability(declaredNullability, promotedBound);
   }
 
   /// Gets a new [TypeParameterType] with given [typeParameterTypeNullability].
@@ -9659,7 +11445,7 @@
     // non-nullable types can be passed in for the type parameter, making the
     // corresponding type parameter types 'undetermined.'  Otherwise, the
     // nullability matches that of the bound.
-    DartType bound = typeParameter.bound;
+    DartType? bound = typeParameter.bound;
     if (bound == null) {
       throw new StateError("Can't compute nullability from an absent bound.");
     }
@@ -9670,9 +11456,9 @@
     // other ways for such a dependency to exist, they should be checked here.
     bool nullabilityDependsOnItself = false;
     {
-      DartType type = typeParameter.bound;
+      DartType? type = typeParameter.bound;
       while (type is FutureOrType) {
-        type = (type as FutureOrType).typeArgument;
+        type = type.typeArgument;
       }
       if (type is TypeParameterType && type.parameter == typeParameter) {
         // Intersection types can't appear in the bound.
@@ -9701,7 +11487,7 @@
   /// is null), the nullability of the intersection type is simply
   /// [typeParameterTypeNullability].
   static Nullability getNullability(
-      Nullability typeParameterTypeNullability, DartType promotedBound) {
+      Nullability typeParameterTypeNullability, DartType? promotedBound) {
     // If promotedBound is null, getNullability simply returns the nullability
     // of the type parameter type.
     Nullability lhsNullability = typeParameterTypeNullability;
@@ -9872,7 +11658,7 @@
       printer.writeTypeParameterName(parameter);
       printer.write(nullabilityToString(declaredNullability));
       printer.write(" & ");
-      printer.writeType(promotedBound);
+      printer.writeType(promotedBound!);
       printer.write(')');
       printer.write(nullabilityToString(nullability));
     } else {
@@ -10006,20 +11792,22 @@
   /// annotations if needed.
   List<Expression> annotations = const <Expression>[];
 
-  String name; // Cosmetic name.
+  String? name; // Cosmetic name.
 
   /// The bound on the type variable.
   ///
   /// Should not be null except temporarily during IR construction.  Should
   /// be set to the root class for type parameters without an explicit bound.
-  DartType bound;
+  // TODO(johnniwinther): Can we make this late non-nullable?
+  DartType? bound;
 
   /// The default value of the type variable. It is used to provide the
   /// corresponding missing type argument in type annotations and as the
   /// fall-back type value in type inference at compile time. At run time,
   /// [defaultType] is used by the backends in place of the missing type
   /// argument of a dynamic invocation of a generic function.
-  DartType defaultType;
+  // TODO(johnniwinther): Can we make this late non-nullable?
+  DartType? defaultType;
 
   /// Describes variance of the type parameter w.r.t. declaration on which it is
   /// defined. For classes, if variance is not explicitly set, the type
@@ -10027,11 +11815,11 @@
   /// on the lattice is equivalent to [Variance.covariant]. For typedefs, it's
   /// the variance of the type parameters in the type term on the r.h.s. of the
   /// typedef.
-  int _variance;
+  int? _variance;
 
   int get variance => _variance ?? Variance.covariant;
 
-  void set variance(int newVariance) => _variance = newVariance;
+  void set variance(int? newVariance) => _variance = newVariance;
 
   bool get isLegacyCovariant => _variance == null;
 
@@ -10063,24 +11851,55 @@
     annotations.add(annotation..parent = this);
   }
 
+  @override
   R accept<R>(TreeVisitor<R> v) => v.visitTypeParameter(this);
 
-  visitChildren(Visitor v) {
+  @override
+  R accept1<R, A>(TreeVisitor1<R, A> v, A arg) =>
+      v.visitTypeParameter(this, arg);
+
+  @override
+  void visitChildren(Visitor v) {
     visitList(annotations, v);
-    bound.accept(v);
+    bound?.accept(v);
     defaultType?.accept(v);
   }
 
-  transformChildren(Transformer v) {
-    transformList(annotations, v, this);
-    bound = v.visitDartType(bound);
+  @override
+  void transformChildren(Transformer v) {
+    v.transformList(annotations, this);
+    if (bound != null) {
+      bound = v.visitDartType(bound!);
+    }
     if (defaultType != null) {
-      defaultType = v.visitDartType(defaultType);
+      defaultType = v.visitDartType(defaultType!);
+    }
+  }
+
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    v.transformExpressionList(annotations, this);
+    if (bound != null) {
+      DartType newBound = v.visitDartType(bound!, dummyDartType);
+      if (identical(newBound, dummyDartType)) {
+        bound = null;
+      } else {
+        bound = newBound;
+      }
+    }
+    if (defaultType != null) {
+      DartType newDefaultType = v.visitDartType(defaultType!, dummyDartType);
+      if (identical(newDefaultType, dummyDartType)) {
+        defaultType = null;
+      } else {
+        defaultType = newDefaultType;
+      }
     }
   }
 
   /// Returns a possibly synthesized name for this type parameter, consistent
   /// with the names used across all [toString] calls.
+  @override
   String toString() {
     return "TypeParameter(${toStringInternal()})";
   }
@@ -10098,7 +11917,8 @@
   final List<DartType> typeArguments;
 
   Supertype(Class classNode, List<DartType> typeArguments)
-      : this.byReference(getClassReference(classNode), typeArguments);
+      : this.byReference(
+            getNonNullableClassReference(classNode), typeArguments);
 
   Supertype.byReference(this.className, this.typeArguments);
 
@@ -10273,6 +12093,7 @@
 
 class StringConstant extends PrimitiveConstant<String> {
   StringConstant(String value) : super(value) {
+    // ignore: unnecessary_null_comparison
     assert(value != null);
   }
 
@@ -10322,6 +12143,7 @@
   @override
   void toTextInternal(AstPrinter printer) {
     printer.write('#');
+    // ignore: unnecessary_null_comparison
     if (printer.includeAuxiliaryProperties && libraryReference != null) {
       printer.write(libraryNameToString(libraryReference.asLibrary));
       printer.write('::');
@@ -10368,11 +12190,9 @@
   @override
   String toString() => 'MapConstant(${toStringInternal()})';
 
-  int _cachedHashCode;
-  int get hashCode {
-    return _cachedHashCode ??= _Hash.combine2Finish(
-        keyType.hashCode, valueType.hashCode, _Hash.combineListHash(entries));
-  }
+  @override
+  late final int hashCode = _Hash.combine2Finish(
+      keyType.hashCode, valueType.hashCode, _Hash.combineListHash(entries));
 
   bool operator ==(Object other) =>
       identical(this, other) ||
@@ -10448,11 +12268,9 @@
   @override
   String toString() => 'ListConstant(${toStringInternal()})';
 
-  int _cachedHashCode;
-  int get hashCode {
-    return _cachedHashCode ??= _Hash.combineFinish(
-        typeArgument.hashCode, _Hash.combineListHash(entries));
-  }
+  @override
+  late final int hashCode = _Hash.combineFinish(
+      typeArgument.hashCode, _Hash.combineListHash(entries));
 
   bool operator ==(Object other) =>
       identical(this, other) ||
@@ -10497,11 +12315,9 @@
   @override
   String toString() => 'SetConstant(${toStringInternal()})';
 
-  int _cachedHashCode;
-  int get hashCode {
-    return _cachedHashCode ??= _Hash.combineFinish(
-        typeArgument.hashCode, _Hash.combineListHash(entries));
-  }
+  @override
+  late final int hashCode = _Hash.combineFinish(
+      typeArgument.hashCode, _Hash.combineListHash(entries));
 
   bool operator ==(Object other) =>
       identical(this, other) ||
@@ -10556,13 +12372,9 @@
   @override
   String toString() => 'InstanceConstant(${toStringInternal()})';
 
-  int _cachedHashCode;
-  int get hashCode {
-    return _cachedHashCode ??= _Hash.combine2Finish(
-        classReference.hashCode,
-        listHashCode(typeArguments),
-        _Hash.combineMapHashUnordered(fieldValues));
-  }
+  @override
+  late final int hashCode = _Hash.combine2Finish(classReference.hashCode,
+      listHashCode(typeArguments), _Hash.combineMapHashUnordered(fieldValues));
 
   bool operator ==(Object other) {
     return identical(this, other) ||
@@ -10630,7 +12442,7 @@
 
   TearOffConstant.byReference(this.procedureReference);
 
-  Procedure get procedure => procedureReference?.asProcedure;
+  Procedure get procedure => procedureReference.asProcedure;
 
   visitChildren(Visitor v) {
     procedureReference.asProcedure.acceptReference(v);
@@ -10655,7 +12467,7 @@
   }
 
   FunctionType getType(StaticTypeContext context) {
-    return procedure.function.computeFunctionType(context.nonNullable);
+    return procedure.function!.computeFunctionType(context.nonNullable);
   }
 }
 
@@ -10694,7 +12506,7 @@
   final Expression expression;
 
   UnevaluatedConstant(this.expression) {
-    expression?.parent = null;
+    expression.parent = null;
   }
 
   visitChildren(Visitor v) {
@@ -10744,7 +12556,7 @@
   ///
   /// Note that this field can be null, and by convention should be null if the
   /// list is empty.
-  List<String> problemsAsJson;
+  List<String>? problemsAsJson;
 
   final List<Library> libraries;
 
@@ -10759,19 +12571,19 @@
       <String, MetadataRepository<dynamic>>{};
 
   /// Reference to the main method in one of the libraries.
-  Reference _mainMethodName;
-  Reference get mainMethodName => _mainMethodName;
-  NonNullableByDefaultCompiledMode _mode;
+  Reference? _mainMethodName;
+  Reference? get mainMethodName => _mainMethodName;
+  NonNullableByDefaultCompiledMode? _mode;
   NonNullableByDefaultCompiledMode get mode {
     return _mode ?? NonNullableByDefaultCompiledMode.Weak;
   }
 
-  NonNullableByDefaultCompiledMode get modeRaw => _mode;
+  NonNullableByDefaultCompiledMode? get modeRaw => _mode;
 
   Component(
-      {CanonicalName nameRoot,
-      List<Library> libraries,
-      Map<Uri, Source> uriToSource})
+      {CanonicalName? nameRoot,
+      List<Library>? libraries,
+      Map<Uri, Source>? uriToSource})
       : root = nameRoot ?? new CanonicalName.root(),
         libraries = libraries ?? <Library>[],
         uriToSource = uriToSource ?? <Uri, Source>{} {
@@ -10779,13 +12591,14 @@
   }
 
   void adoptChildren() {
+    // ignore: unnecessary_null_comparison
     if (libraries != null) {
       for (int i = 0; i < libraries.length; ++i) {
         // The libraries are owned by this component, and so are their canonical
         // names if they exist.
         Library library = libraries[i];
         library.parent = this;
-        CanonicalName name = library.reference.canonicalName;
+        CanonicalName? name = library.reference.canonicalName;
         if (name != null && name.parent != root) {
           root.adoptChild(name);
         }
@@ -10848,7 +12661,7 @@
     root.unbindAll();
   }
 
-  Procedure get mainMethod => mainMethodName?.asProcedure;
+  Procedure? get mainMethod => mainMethodName?.asProcedure;
 
   void setMainMethodAndMode(Reference main, bool overwriteMainIfSet,
       NonNullableByDefaultCompiledMode mode) {
@@ -10858,21 +12671,33 @@
     _mode = mode;
   }
 
+  @override
   R accept<R>(TreeVisitor<R> v) => v.visitComponent(this);
 
-  visitChildren(Visitor v) {
+  @override
+  R accept1<R, A>(TreeVisitor1<R, A> v, A arg) => v.visitComponent(this, arg);
+
+  @override
+  void visitChildren(Visitor v) {
     visitList(libraries, v);
     mainMethod?.acceptReference(v);
   }
 
-  transformChildren(Transformer v) {
-    transformList(libraries, v, this);
+  @override
+  void transformChildren(Transformer v) {
+    v.transformList(libraries, this);
   }
 
+  @override
+  void transformOrRemoveChildren(RemovingTransformer v) {
+    v.transformLibraryList(libraries, this);
+  }
+
+  @override
   Component get enclosingComponent => this;
 
   /// Translates an offset to line and column numbers in the given file.
-  Location getLocation(Uri file, int offset) {
+  Location? getLocation(Uri? file, int offset) {
     return uriToSource[file]?.getLocation(file, offset);
   }
 
@@ -10905,7 +12730,8 @@
 /// A tuple with file, line, and column number, for displaying human-readable
 /// locations.
 class Location {
-  final Uri file;
+  // TODO(johnniwinther): Make this non-nullable.
+  final Uri? file;
   final int line; // 1-based.
   final int column; // 1-based.
 
@@ -11030,49 +12856,6 @@
   }
 }
 
-void transformTypeList(List<DartType> nodes, Transformer visitor) {
-  int storeIndex = 0;
-  for (int i = 0; i < nodes.length; ++i) {
-    DartType result = visitor.visitDartType(nodes[i]);
-    if (result != null) {
-      nodes[storeIndex] = result;
-      ++storeIndex;
-    }
-  }
-  if (storeIndex < nodes.length) {
-    nodes.length = storeIndex;
-  }
-}
-
-void transformSupertypeList(List<Supertype> nodes, Transformer visitor) {
-  int storeIndex = 0;
-  for (int i = 0; i < nodes.length; ++i) {
-    Supertype result = visitor.visitSupertype(nodes[i]);
-    if (result != null) {
-      nodes[storeIndex] = result;
-      ++storeIndex;
-    }
-  }
-  if (storeIndex < nodes.length) {
-    nodes.length = storeIndex;
-  }
-}
-
-void transformList(List<TreeNode> nodes, Transformer visitor, TreeNode parent) {
-  int storeIndex = 0;
-  for (int i = 0; i < nodes.length; ++i) {
-    TreeNode result = nodes[i].accept(visitor);
-    if (result != null) {
-      nodes[storeIndex] = result;
-      result.parent = parent;
-      ++storeIndex;
-    }
-  }
-  if (storeIndex < nodes.length) {
-    nodes.length = storeIndex;
-  }
-}
-
 class _ChildReplacer extends Transformer {
   final TreeNode child;
   final TreeNode replacement;
@@ -11080,7 +12863,7 @@
   _ChildReplacer(this.child, this.replacement);
 
   @override
-  defaultTreeNode(TreeNode node) {
+  TreeNode defaultTreeNode(TreeNode node) {
     if (node == child) {
       return replacement;
     } else {
@@ -11090,31 +12873,35 @@
 }
 
 class Source {
-  final List<int> lineStarts;
+  final List<int>? lineStarts;
 
   /// A UTF8 encoding of the original source file.
-  final List<int> source;
+  final List<int>? source;
 
   final Uri importUri;
 
   final Uri fileUri;
 
-  Set<Reference> constantCoverageConstructors;
+  Set<Reference>? constantCoverageConstructors;
 
-  String cachedText;
+  String? cachedText;
 
   Source(this.lineStarts, this.source, this.importUri, this.fileUri);
 
   /// Return the text corresponding to [line] which is a 1-based line
   /// number. The returned line contains no line separators.
-  String getTextLine(int line) {
+  String? getTextLine(int line) {
+    List<int>? lineStarts = this.lineStarts;
     if (source == null ||
-        source.isEmpty ||
+        source!.isEmpty ||
         lineStarts == null ||
-        lineStarts.isEmpty) return null;
+        lineStarts.isEmpty) {
+      return null;
+    }
     RangeError.checkValueInInterval(line, 1, lineStarts.length, 'line');
 
-    cachedText ??= utf8.decode(source, allowMalformed: true);
+    String cachedText =
+        this.cachedText ??= utf8.decode(source!, allowMalformed: true);
     // -1 as line numbers start at 1.
     int index = line - 1;
     if (index + 1 == lineStarts.length) {
@@ -11135,7 +12922,8 @@
   }
 
   /// Translates an offset to 1-based line and column numbers in the given file.
-  Location getLocation(Uri file, int offset) {
+  Location getLocation(Uri? file, int offset) {
+    List<int>? lineStarts = this.lineStarts;
     if (lineStarts == null || lineStarts.isEmpty) {
       return new Location(file, TreeNode.noOffset, TreeNode.noOffset);
     }
@@ -11163,6 +12951,7 @@
   /// has no lines.
   /// Throws [RangeError] if line or calculated offset are out of range.
   int getOffset(int line, int column) {
+    List<int>? lineStarts = this.lineStarts;
     if (lineStarts == null || lineStarts.isEmpty) {
       return -1;
     }
@@ -11177,11 +12966,11 @@
 /// ProcedureKind.
 ///
 /// Returns `null` if the member is `null`.
-Reference getMemberReferenceBasedOnProcedureKind(
-    Member member, ProcedureKind kind) {
+Reference? getMemberReferenceBasedOnProcedureKind(
+    Member? member, ProcedureKind kind) {
   if (member == null) return null;
   if (member is Field) {
-    if (kind == ProcedureKind.Setter) return member.setterReference;
+    if (kind == ProcedureKind.Setter) return member.setterReference!;
     return member.getterReference;
   }
   return member.reference;
@@ -11191,8 +12980,12 @@
 ///
 /// Returns `null` if the member is `null`.
 /// TODO(jensj): Should it be called NotSetter instead of Getter?
-Reference getMemberReferenceGetter(Member member) {
+Reference? getMemberReferenceGetter(Member? member) {
   if (member == null) return null;
+  return getNonNullableMemberReferenceGetter(member);
+}
+
+Reference getNonNullableMemberReferenceGetter(Member member) {
   if (member is Field) return member.getterReference;
   return member.reference;
 }
@@ -11200,26 +12993,35 @@
 /// Returns the setter [Reference] object for the given member.
 ///
 /// Returns `null` if the member is `null`.
-Reference getMemberReferenceSetter(Member member) {
+Reference? getMemberReferenceSetter(Member? member) {
   if (member == null) return null;
-  if (member is Field) return member.setterReference;
+  return getNonNullableMemberReferenceSetter(member);
+}
+
+Reference getNonNullableMemberReferenceSetter(Member member) {
+  if (member is Field) return member.setterReference!;
   return member.reference;
 }
 
 /// Returns the [Reference] object for the given class.
 ///
 /// Returns `null` if the class is `null`.
-Reference getClassReference(Class class_) {
+Reference? getClassReference(Class? class_) {
   return class_?.reference;
 }
 
+/// Returns the [Reference] object for the given class.
+Reference getNonNullableClassReference(Class class_) {
+  return class_.reference;
+}
+
 /// Returns the canonical name of [member], or throws an exception if the
 /// member has not been assigned a canonical name yet.
 ///
 /// Returns `null` if the member is `null`.
-CanonicalName getCanonicalNameOfMemberGetter(Member member) {
+CanonicalName? getCanonicalNameOfMemberGetter(Member? member) {
   if (member == null) return null;
-  CanonicalName canonicalName;
+  CanonicalName? canonicalName;
   if (member is Field) {
     canonicalName = member.getterCanonicalName;
   } else {
@@ -11235,9 +13037,9 @@
 /// member has not been assigned a canonical name yet.
 ///
 /// Returns `null` if the member is `null`.
-CanonicalName getCanonicalNameOfMemberSetter(Member member) {
+CanonicalName? getCanonicalNameOfMemberSetter(Member? member) {
   if (member == null) return null;
-  CanonicalName canonicalName;
+  CanonicalName? canonicalName;
   if (member is Field) {
     canonicalName = member.setterCanonicalName;
   } else {
@@ -11253,7 +13055,7 @@
 /// class has not been assigned a canonical name yet.
 ///
 /// Returns `null` if the class is `null`.
-CanonicalName getCanonicalNameOfClass(Class class_) {
+CanonicalName? getCanonicalNameOfClass(Class? class_) {
   if (class_ == null) return null;
   if (class_.canonicalName == null) {
     throw '$class_ has no canonical name';
@@ -11265,7 +13067,7 @@
 /// class has not been assigned a canonical name yet.
 ///
 /// Returns `null` if the extension is `null`.
-CanonicalName getCanonicalNameOfExtension(Extension extension) {
+CanonicalName? getCanonicalNameOfExtension(Extension? extension) {
   if (extension == null) return null;
   if (extension.canonicalName == null) {
     throw '$extension has no canonical name';
@@ -11277,7 +13079,7 @@
 /// library has not been assigned a canonical name yet.
 ///
 /// Returns `null` if the library is `null`.
-CanonicalName getCanonicalNameOfLibrary(Library library) {
+CanonicalName? getCanonicalNameOfLibrary(Library? library) {
   if (library == null) return null;
   if (library.canonicalName == null) {
     throw '$library has no canonical name';
@@ -11356,9 +13158,12 @@
     return hash;
   }
 
-  static int combineMapHashUnordered(Map map, [int hash = 2]) {
+  static int combineMapHashUnordered(Map? map, [int hash = 2]) {
     if (map == null || map.isEmpty) return hash;
-    List<int> entryHashes = List.filled(map.length, null);
+    List<int> entryHashes = List.filled(
+        map.length,
+        // `-1` is used as a dummy default value.
+        -1);
     int i = 0;
     for (core.MapEntry entry in map.entries) {
       entryHashes[i++] = combine(entry.key.hashCode, entry.value.hashCode);
@@ -11375,7 +13180,7 @@
   }
 }
 
-int listHashCode(List list) {
+int listHashCode(List<Object> list) {
   return _Hash.finish(_Hash.combineListHash(list));
 }
 
@@ -11417,7 +13222,7 @@
 /// typedef has not been assigned a canonical name yet.
 ///
 /// Returns `null` if the typedef is `null`.
-CanonicalName getCanonicalNameOfTypedef(Typedef typedef_) {
+CanonicalName? getCanonicalNameOfTypedef(Typedef? typedef_) {
   if (typedef_ == null) return null;
   if (typedef_.canonicalName == null) {
     throw '$typedef_ has no canonical name';
@@ -11430,7 +13235,8 @@
 /// static analysis and runtime behavior of the library are unaffected.
 const Null informative = null;
 
-Location _getLocationInComponent(Component component, Uri fileUri, int offset) {
+Location? _getLocationInComponent(
+    Component? component, Uri? fileUri, int offset) {
   if (component != null) {
     return component.getLocation(fileUri, offset);
   } else {
@@ -11471,13 +13277,11 @@
 List<DartType> getAsTypeArguments(
     List<TypeParameter> typeParameters, Library library) {
   if (typeParameters.isEmpty) return const <DartType>[];
-  List<DartType> result =
-      new List<DartType>.filled(typeParameters.length, null, growable: false);
-  for (int i = 0; i < result.length; ++i) {
-    result[i] = new TypeParameterType.withDefaultNullabilityForLibrary(
-        typeParameters[i], library);
-  }
-  return result;
+  return new List<DartType>.generate(
+      typeParameters.length,
+      (int i) => new TypeParameterType.withDefaultNullabilityForLibrary(
+          typeParameters[i], library),
+      growable: false);
 }
 
 class Version extends Object {
@@ -11485,7 +13289,9 @@
   final int minor;
 
   const Version(this.major, this.minor)
+      // ignore: unnecessary_null_comparison
       : assert(major != null),
+        // ignore: unnecessary_null_comparison
         assert(minor != null);
 
   bool operator <(Version other) {
@@ -11544,15 +13350,218 @@
   }
 }
 
-/// Non-nullable `DartType` value to be used as a dummy initial value for the
-/// `List.filled` constructor.
-const DartType dartTypeDummy = const DynamicType();
+/// Non-nullable [DartType] dummy value.
+///
+/// This is used as the removal sentinel in [RemovingTransformer] and can be
+/// used for instance as a dummy initial value for the `List.filled`
+/// constructor.
+final DartType dummyDartType = new DynamicType();
 
-/// Non-nullable `NamedType` value to be used as a dummy initial value for the
-/// `List.filled` constructor.
-const NamedType namedTypeDummy =
-    const NamedType('', dartTypeDummy, isRequired: false);
+/// Non-nullable [Supertype] dummy value.
+///
+/// This is used as the removal sentinel in [RemovingTransformer] and can be
+/// used for instance as a dummy initial value for the `List.filled`
+/// constructor.
+final Supertype dummySupertype = new Supertype(dummyClass, const []);
 
-/// Non-nullable `Member` value to be used as a dummy initial value for the
+/// Non-nullable [NamedType] dummy value.
+///
+/// This is used as the removal sentinel in [RemovingTransformer] and can be
+/// used for instance as a dummy initial value for the `List.filled`
+/// constructor.
+final NamedType dummyNamedType =
+    new NamedType('', dummyDartType, isRequired: false);
+
+/// Non-nullable [Uri] dummy value.
+final Uri dummyUri = new Uri(scheme: 'dummy');
+
+/// Non-nullable [Name] dummy value.
+final Name dummyName = new _PublicName('');
+
+/// Non-nullable [Library] dummy value.
+///
+/// This is used as the removal sentinel in [RemovingTransformer] and can be
+/// used for instance as a dummy initial value for the `List.filled`
+/// constructor.
+final Library dummyLibrary = new Library(dummyUri);
+
+/// Non-nullable [LibraryDependency] dummy value.
+///
+/// This is used as the removal sentinel in [RemovingTransformer] and can be
+/// used for instance as a dummy initial value for the `List.filled`
+/// constructor.
+final LibraryDependency dummyLibraryDependency =
+    new LibraryDependency.import(dummyLibrary);
+
+/// Non-nullable [Combinator] dummy value.
+///
+/// This is used as the removal sentinel in [RemovingTransformer] and can be
+/// used for instance as a dummy initial value for the `List.filled`
+/// constructor.
+final Combinator dummyCombinator = new Combinator(false, const []);
+
+/// Non-nullable [LibraryPart] dummy value.
+///
+/// This is used as the removal sentinel in [RemovingTransformer] and can be
+/// used for instance as a dummy initial value for the `List.filled`
+/// constructor.
+final LibraryPart dummyLibraryPart = new LibraryPart(const [], '');
+
+/// Non-nullable [Class] dummy value.
+///
+/// This is used as the removal sentinel in [RemovingTransformer] and can be
+/// used for instance as a dummy initial value for the `List.filled`
+/// constructor.
+final Class dummyClass = new Class();
+
+/// Non-nullable [Constructor] dummy value.
+///
+/// This is used as the removal sentinel in [RemovingTransformer] and can be
+/// used for instance as a dummy initial value for the `List.filled`
+/// constructor.
+final Constructor dummyConstructor = new Constructor(dummyFunctionNode);
+
+/// Non-nullable [Extension] dummy value.
+///
+/// This is used as the removal sentinel in [RemovingTransformer] and can be
+/// used for instance as a dummy initial value for the `List.filled`
+/// constructor.
+final Extension dummyExtension = new Extension();
+
+/// Non-nullable [Member] dummy value.
+///
+/// This can be used for instance as a dummy initial value for the
 /// `List.filled` constructor.
 final Member dummyMember = new Field.mutable(new _PublicName(''));
+
+/// Non-nullable [Procedure] dummy value.
+///
+/// This is used as the removal sentinel in [RemovingTransformer] and can be
+/// used for instance as a dummy initial value for the `List.filled`
+/// constructor.
+final Procedure dummyProcedure =
+    new Procedure(dummyName, ProcedureKind.Method, dummyFunctionNode);
+
+/// Non-nullable [Field] dummy value.
+///
+/// This is used as the removal sentinel in [RemovingTransformer] and can be
+/// used for instance as a dummy initial value for the `List.filled`
+/// constructor.
+final Field dummyField = new Field.mutable(dummyName);
+
+/// Non-nullable [RedirectingFactoryConstructor] dummy value.
+///
+/// This is used as the removal sentinel in [RemovingTransformer] and can be
+/// used for instance as a dummy initial value for the `List.filled`
+/// constructor.
+final RedirectingFactoryConstructor dummyRedirectingFactoryConstructor =
+    new RedirectingFactoryConstructor(null);
+
+/// Non-nullable [Typedef] dummy value.
+///
+/// This is used as the removal sentinel in [RemovingTransformer] and can be
+/// used for instance as a dummy initial value for the `List.filled`
+/// constructor.
+final Typedef dummyTypedef = new Typedef('', null);
+
+/// Non-nullable [Initializer] dummy value.
+///
+/// This is used as the removal sentinel in [RemovingTransformer] and can be
+/// used for instance as a dummy initial value for the `List.filled`
+/// constructor.
+final Initializer dummyInitializer = new InvalidInitializer();
+
+/// Non-nullable [FunctionNode] dummy value.
+///
+/// This is used as the removal sentinel in [RemovingTransformer] and can be
+/// used for instance as a dummy initial value for the `List.filled`
+/// constructor.
+final FunctionNode dummyFunctionNode = new FunctionNode(null);
+
+/// Non-nullable [Statement] dummy value.
+///
+/// This is used as the removal sentinel in [RemovingTransformer] and can be
+/// used for instance as a dummy initial value for the `List.filled`
+/// constructor.
+final Statement dummyStatement = new EmptyStatement();
+
+/// Non-nullable [Expression] dummy value.
+///
+/// This is used as the removal sentinel in [RemovingTransformer] and can be
+/// used for instance as a dummy initial value for the `List.filled`
+/// constructor.
+final Expression dummyExpression = new NullLiteral();
+
+/// Non-nullable [NamedExpression] dummy value.
+///
+/// This is used as the removal sentinel in [RemovingTransformer] and can be
+/// used for instance as a dummy initial value for the `List.filled`
+/// constructor.
+final NamedExpression dummyNamedExpression =
+    new NamedExpression('', dummyExpression);
+
+/// Non-nullable [VariableDeclaration] dummy value.
+///
+/// This is used as the removal sentinel in [RemovingTransformer] and can be
+/// used for instance as a dummy initial value for the `List.filled`
+/// constructor.
+final VariableDeclaration dummyVariableDeclaration =
+    new VariableDeclaration(null);
+
+/// Non-nullable [TypeParameter] dummy value.
+///
+/// This is used as the removal sentinel in [RemovingTransformer] and can be
+/// used for instance as a dummy initial value for the `List.filled`
+/// constructor.
+final TypeParameter dummyTypeParameter = new TypeParameter();
+
+/// Non-nullable [MapEntry] dummy value.
+///
+/// This is used as the removal sentinel in [RemovingTransformer] and can be
+/// used for instance as a dummy initial value for the `List.filled`
+/// constructor.
+final MapEntry dummyMapEntry = new MapEntry(dummyExpression, dummyExpression);
+
+/// Non-nullable [Arguments] dummy value.
+///
+/// This is used as the removal sentinel in [RemovingTransformer] and can be
+/// used for instance as a dummy initial value for the `List.filled`
+/// constructor.
+final Arguments dummyArguments = new Arguments(const []);
+
+/// Non-nullable [AssertStatement] dummy value.
+///
+/// This is used as the removal sentinel in [RemovingTransformer] and can be
+/// used for instance as a dummy initial value for the `List.filled`
+/// constructor.
+final AssertStatement dummyAssertStatement = new AssertStatement(
+    dummyExpression,
+    conditionStartOffset: TreeNode.noOffset,
+    conditionEndOffset: TreeNode.noOffset);
+
+/// Non-nullable [SwitchCase] dummy value.
+///
+/// This is used as the removal sentinel in [RemovingTransformer] and can be
+/// used for instance as a dummy initial value for the `List.filled`
+/// constructor.
+final SwitchCase dummySwitchCase = new SwitchCase.empty();
+
+/// Non-nullable [Catch] dummy value.
+///
+/// This is used as the removal sentinel in [RemovingTransformer] and can be
+/// used for instance as a dummy initial value for the `List.filled`
+/// constructor.
+final Catch dummyCatch = new Catch(null, dummyStatement);
+
+/// Sentinel value used to signal that a node cannot be removed through the
+/// [RemovingTransformer].
+const Null cannotRemoveSentinel = null;
+
+/// Helper that can be used in asserts to check that [list] is mutable by
+/// adding and removing [dummyElement].
+bool checkListIsMutable<E>(List<E> list, E dummyElement) {
+  list
+    ..add(dummyElement)
+    ..removeLast();
+  return true;
+}
diff --git a/pkg/kernel/lib/canonical_name.dart b/pkg/kernel/lib/canonical_name.dart
index 70f3004..af7344e 100644
--- a/pkg/kernel/lib/canonical_name.dart
+++ b/pkg/kernel/lib/canonical_name.dart
@@ -2,8 +2,6 @@
 // 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.
 
-// @dart = 2.9
-
 library kernel.canonical_name;
 
 import 'ast.dart';
@@ -69,25 +67,27 @@
 /// The "qualified name" allows a member to have a name that is private to
 /// a library other than the one containing that member.
 class CanonicalName {
-  CanonicalName _parent;
+  CanonicalName? _parent;
 
-  CanonicalName get parent => _parent;
+  CanonicalName? get parent => _parent;
 
   final String name;
-  CanonicalName _nonRootTop;
+  CanonicalName? _nonRootTop;
 
-  Map<String, CanonicalName> _children;
+  Map<String, CanonicalName>? _children;
 
   /// The library, class, or member bound to this name.
-  Reference reference;
+  Reference? reference;
 
   /// Temporary index used during serialization.
   int index = -1;
 
-  CanonicalName._(this._parent, this.name) {
+  CanonicalName._(CanonicalName parent, this.name) : _parent = parent {
+    // ignore: unnecessary_null_comparison
     assert(name != null);
+    // ignore: unnecessary_null_comparison
     assert(parent != null);
-    _nonRootTop = _parent.isRoot ? this : _parent._nonRootTop;
+    _nonRootTop = parent.isRoot ? this : parent._nonRootTop;
   }
 
   CanonicalName.root()
@@ -96,15 +96,16 @@
         name = '';
 
   bool get isRoot => _parent == null;
-  CanonicalName get nonRootTop => _nonRootTop;
+
+  CanonicalName? get nonRootTop => _nonRootTop;
 
   Iterable<CanonicalName> get children =>
       _children?.values ?? const <CanonicalName>[];
 
-  Iterable<CanonicalName> get childrenOrNull => _children?.values;
+  Iterable<CanonicalName>? get childrenOrNull => _children?.values;
 
   bool hasChild(String name) {
-    return _children != null && _children.containsKey(name);
+    return _children != null && _children!.containsKey(name);
   }
 
   CanonicalName getChild(String name) {
@@ -121,32 +122,32 @@
 
   CanonicalName getChildFromQualifiedName(Name name) {
     return name.isPrivate
-        ? getChildFromUri(name.library.importUri).getChild(name.text)
+        ? getChildFromUri(name.library!.importUri).getChild(name.text)
         : getChild(name.text);
   }
 
   CanonicalName getChildFromProcedure(Procedure procedure) {
     return getChild(getProcedureQualifier(procedure))
-        .getChildFromQualifiedName(procedure.name);
+        .getChildFromQualifiedName(procedure.name!);
   }
 
   CanonicalName getChildFromField(Field field) {
-    return getChild('@fields').getChildFromQualifiedName(field.name);
+    return getChild('@fields').getChildFromQualifiedName(field.name!);
   }
 
   CanonicalName getChildFromFieldSetter(Field field) {
-    return getChild('@=fields').getChildFromQualifiedName(field.name);
+    return getChild('@=fields').getChildFromQualifiedName(field.name!);
   }
 
   CanonicalName getChildFromConstructor(Constructor constructor) {
     return getChild('@constructors')
-        .getChildFromQualifiedName(constructor.name);
+        .getChildFromQualifiedName(constructor.name!);
   }
 
   CanonicalName getChildFromRedirectingFactoryConstructor(
       RedirectingFactoryConstructor redirectingFactoryConstructor) {
     return getChild('@factories')
-        .getChildFromQualifiedName(redirectingFactoryConstructor.name);
+        .getChildFromQualifiedName(redirectingFactoryConstructor.name!);
   }
 
   CanonicalName getChildFromFieldWithName(Name name) {
@@ -175,26 +176,27 @@
   /// the same name.
   void adoptChild(CanonicalName child) {
     if (child._parent == this) return;
-    if (_children != null && _children.containsKey(child.name)) {
+    if (_children != null && _children!.containsKey(child.name)) {
       throw 'Cannot add a child to $this because this name already has a '
           'child named ${child.name}';
     }
-    child._parent.removeChild(child.name);
+    child._parent?.removeChild(child.name);
     child._parent = this;
-    if (_children == null) _children = <String, CanonicalName>{};
-    _children[child.name] = child;
+    _children ??= <String, CanonicalName>{};
+    _children![child.name] = child;
   }
 
   void removeChild(String name) {
     if (_children != null) {
-      _children.remove(name);
-      if (_children.isEmpty) {
+      _children!.remove(name);
+      if (_children!.isEmpty) {
         _children = null;
       }
     }
   }
 
   void bindTo(Reference target) {
+    // ignore: unnecessary_null_comparison
     if (target == null) {
       throw '$this cannot be bound to null';
     }
@@ -217,25 +219,25 @@
     // canonical name tree. We need to establish better invariants about the
     // state of the canonical name tree, since for instance [unbindAll] doesn't
     // remove unneeded leaf nodes.
-    _parent.removeChild(name);
+    _parent?.removeChild(name);
   }
 
   void _unbindInternal() {
     if (reference == null) return;
-    assert(reference.canonicalName == this);
-    if (reference.node is Class) {
+    assert(reference!.canonicalName == this);
+    if (reference!.node is Class) {
       // TODO(jensj): Get rid of this. This is only needed because pkg:vm does
       // weird stuff in transformations. `unbind` should probably be private.
-      Class c = reference.node;
+      Class c = reference!.asClass;
       c.ensureLoaded();
     }
-    reference.canonicalName = null;
+    reference!.canonicalName = null;
     reference = null;
   }
 
   void unbindAll() {
     _unbindInternal();
-    Iterable<CanonicalName> children_ = childrenOrNull;
+    Iterable<CanonicalName>? children_ = childrenOrNull;
     if (children_ != null) {
       for (CanonicalName child in children_) {
         child.unbindAll();
@@ -246,8 +248,8 @@
   String toString() => _parent == null ? 'root' : '$parent::$name';
   String toStringInternal() {
     if (isRoot) return "";
-    if (parent.isRoot) return "$name";
-    return "${parent.toStringInternal()}::$name";
+    if (parent!.isRoot) return "$name";
+    return "${parent!.toStringInternal()}::$name";
   }
 
   Reference getReference() {
diff --git a/pkg/kernel/lib/class_hierarchy.dart b/pkg/kernel/lib/class_hierarchy.dart
index 08ed371..0c6e3f3 100644
--- a/pkg/kernel/lib/class_hierarchy.dart
+++ b/pkg/kernel/lib/class_hierarchy.dart
@@ -8,7 +8,6 @@
 import 'dart:math';
 import 'dart:typed_data';
 
-// ignore: import_of_legacy_library_into_null_safe
 import 'ast.dart' hide MapEntry;
 import 'core_types.dart';
 import 'type_algebra.dart';
@@ -283,7 +282,7 @@
   /// [getDeclaredMembers] and [getInterfaceMembers].
   static int compareMembers(Member first, Member second) {
     if (first == second) return 0;
-    return compareNames(first.name, second.name);
+    return compareNames(first.name!, second.name!);
   }
 
   /// Compares names, using the same sort order as [getDeclaredMembers] and
@@ -329,7 +328,7 @@
     while (low <= high) {
       int mid = low + ((high - low) >> 1);
       Member pivot = members[mid];
-      int comparison = compareNames(name, pivot.name);
+      int comparison = compareNames(name, pivot.name!);
       if (comparison < 0) {
         high = mid - 1;
       } else if (comparison > 0) {
@@ -418,9 +417,9 @@
   Member? getSingleTargetForInterfaceInvocation(Member interfaceTarget,
       {bool setter: false}) {
     if (invalidated) throw "This data structure has been invalidated";
-    Name name = interfaceTarget.name;
+    Name name = interfaceTarget.name!;
     Member? target = null;
-    ClassSet subtypes = getSubtypesOf(interfaceTarget.enclosingClass);
+    ClassSet subtypes = getSubtypesOf(interfaceTarget.enclosingClass!);
     for (Class c in subtypes) {
       if (!c.isAbstract) {
         Member? candidate =
@@ -591,8 +590,14 @@
         heap.add(infoFor(supertype.classNode));
       }
 
-      if (classNode.supertype != null) addToHeap(classNode.supertype);
-      if (classNode.mixedInType != null) addToHeap(classNode.mixedInType);
+      Supertype? supertype = classNode.supertype;
+      if (supertype != null) {
+        addToHeap(supertype);
+      }
+      Supertype? mixedInType = classNode.mixedInType;
+      if (mixedInType != null) {
+        addToHeap(mixedInType);
+      }
       classNode.implementedTypes.forEach(addToHeap);
     }
     return chain;
@@ -861,11 +866,13 @@
     void removeClass(Class cls) {
       _ClassInfo? info = _infoMap[cls];
       if (info == null) return;
-      if (cls.supertype != null) {
-        _infoMap[cls.supertype.classNode]?.directExtenders.remove(info);
+      Supertype? supertype = cls.supertype;
+      if (supertype != null) {
+        _infoMap[supertype.classNode]?.directExtenders.remove(info);
       }
-      if (cls.mixedInType != null) {
-        _infoMap[cls.mixedInType.classNode]?.directMixers.remove(info);
+      Supertype? mixedInType = cls.mixedInType;
+      if (mixedInType != null) {
+        _infoMap[mixedInType.classNode]?.directMixers.remove(info);
       }
       for (Supertype supertype in cls.implementedTypes) {
         _infoMap[supertype.classNode]?.directImplementers.remove(info);
@@ -1095,12 +1102,14 @@
       }
       _collectSupersForClass(class_);
 
-      if (class_.supertype != null) {
-        _recordSuperTypes(info, class_.supertype);
+      Supertype? supertype = class_.supertype;
+      if (supertype != null) {
+        _recordSuperTypes(info, supertype);
       }
-      if (class_.mixedInType != null) {
+      Supertype? mixedInType = class_.mixedInType;
+      if (mixedInType != null) {
         mixinInferrer?.infer(this, class_);
-        _recordSuperTypes(info, class_.mixedInType);
+        _recordSuperTypes(info, mixedInType);
       }
       for (Supertype supertype in class_.implementedTypes) {
         _recordSuperTypes(info, supertype);
@@ -1169,10 +1178,11 @@
     if (members != null) return members;
 
     List<Member> inherited;
-    if (classNode.supertype == null) {
+    Supertype? supertype = classNode.supertype;
+    if (supertype == null) {
       inherited = const <Member>[];
     } else {
-      Class superClassNode = classNode.supertype.classNode;
+      Class superClassNode = supertype.classNode;
       _ClassInfo superInfo = _infoMap[superClassNode]!;
       inherited =
           _buildImplementedMembers(superClassNode, superInfo, setters: setters);
@@ -1208,7 +1218,7 @@
           setters: setters)) {
         if (mixinMember is! Procedure ||
             (mixinMember is Procedure && !mixinMember.isSynthetic)) {
-          memberMap[mixinMember.name] = mixinMember;
+          memberMap[mixinMember.name!] = mixinMember;
         }
       }
     }
@@ -1217,21 +1227,21 @@
       if (procedure.isStatic) continue;
       if (procedure.kind == ProcedureKind.Setter) {
         if (setters) {
-          memberMap[procedure.name] = procedure;
+          memberMap[procedure.name!] = procedure;
         }
       } else {
         if (!setters) {
-          memberMap[procedure.name] = procedure;
+          memberMap[procedure.name!] = procedure;
         }
       }
     }
     for (Field field in classNode.fields) {
       if (field.isStatic) continue;
       if (!setters) {
-        memberMap[field.name] = field;
+        memberMap[field.name!] = field;
       }
       if (setters && field.hasSetter) {
-        memberMap[field.name] = field;
+        memberMap[field.name!] = field;
       }
     }
 
diff --git a/pkg/kernel/lib/core_types.dart b/pkg/kernel/lib/core_types.dart
index 2fa52ac..edac20b 100644
--- a/pkg/kernel/lib/core_types.dart
+++ b/pkg/kernel/lib/core_types.dart
@@ -4,7 +4,6 @@
 
 library kernel.core_types;
 
-// ignore: import_of_legacy_library_into_null_safe
 import 'ast.dart';
 import 'library_index.dart';
 import 'type_algebra.dart';
@@ -1137,13 +1136,13 @@
     if (type is TypeParameterType &&
         type.promotedBound != null &&
         type.isPotentiallyNonNullable) {
-      return isBottom(type.promotedBound);
+      return isBottom(type.promotedBound!);
     }
 
     // BOTTOM(X extends T) is true iff BOTTOM(T).
     if (type is TypeParameterType && type.isPotentiallyNonNullable) {
       assert(type.promotedBound == null);
-      return isBottom(type.parameter.bound);
+      return isBottom(type.parameter.bound!);
     }
 
     if (type is BottomType) return true;
diff --git a/pkg/kernel/lib/default_language_version.dart b/pkg/kernel/lib/default_language_version.dart
index 5b39ec4..6de3073 100644
--- a/pkg/kernel/lib/default_language_version.dart
+++ b/pkg/kernel/lib/default_language_version.dart
@@ -2,8 +2,6 @@
 // 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.
 
-// @dart = 2.9
-
 // NOTE: THIS FILE IS GENERATED. DO NOT EDIT.
 //
 // Instead modify 'tools/experimental_features.yaml' and run
diff --git a/pkg/kernel/lib/import_table.dart b/pkg/kernel/lib/import_table.dart
index 1cc27c2..d6bf7ee 100644
--- a/pkg/kernel/lib/import_table.dart
+++ b/pkg/kernel/lib/import_table.dart
@@ -4,7 +4,6 @@
 
 library kernel.import_table;
 
-// ignore: import_of_legacy_library_into_null_safe
 import 'ast.dart';
 
 abstract class ImportTable {
@@ -111,7 +110,7 @@
   visitLibrary(Library node) {
     super.visitLibrary(node);
     for (Reference exportedReference in node.additionalExports) {
-      addLibraryImport(exportedReference.node.parent as Library);
+      addLibraryImport(exportedReference.node!.parent as Library);
     }
   }
 
diff --git a/pkg/kernel/lib/library_index.dart b/pkg/kernel/lib/library_index.dart
index 630763d..2619b62 100644
--- a/pkg/kernel/lib/library_index.dart
+++ b/pkg/kernel/lib/library_index.dart
@@ -4,7 +4,6 @@
 
 library kernel.library_index;
 
-// ignore: import_of_legacy_library_into_null_safe
 import 'ast.dart';
 
 /// Provides name-based access to library, class, and member AST nodes.
@@ -142,18 +141,18 @@
       _classes = <String, _MemberTable>{};
       _classes![LibraryIndex.topLevel] = new _MemberTable.topLevel(this);
       for (Class class_ in library.classes) {
-        _classes![class_.name] = new _MemberTable.fromClass(this, class_);
+        _classes![class_.name!] = new _MemberTable.fromClass(this, class_);
       }
       for (Extension extension_ in library.extensions) {
-        _classes![extension_.name] =
+        _classes![extension_.name!] =
             new _MemberTable.fromExtension(this, extension_);
       }
       for (Reference reference in library.additionalExports) {
-        NamedNode node = reference.node;
+        NamedNode? node = reference.node;
         if (node is Class) {
-          _classes![node.name] = new _MemberTable.fromClass(this, node);
+          _classes![node.name!] = new _MemberTable.fromClass(this, node);
         } else if (node is Extension) {
-          _classes![node.name] = new _MemberTable.fromExtension(this, node);
+          _classes![node.name!] = new _MemberTable.fromExtension(this, node);
         }
       }
     }
@@ -222,14 +221,14 @@
 
   String getDisambiguatedName(Member member) {
     if (member is Procedure) {
-      if (member.isGetter) return LibraryIndex.getterPrefix + member.name.text;
-      if (member.isSetter) return LibraryIndex.setterPrefix + member.name.text;
+      if (member.isGetter) return LibraryIndex.getterPrefix + member.name!.text;
+      if (member.isSetter) return LibraryIndex.setterPrefix + member.name!.text;
     }
-    return member.name.text;
+    return member.name!.text;
   }
 
   void _addMember(Member member) {
-    if (member.name.isPrivate && member.name.library != library) {
+    if (member.name!.isPrivate && member.name!.library != library) {
       // Members whose name is private to other libraries cannot currently
       // be found with the LibraryIndex class.
       return;
@@ -252,10 +251,10 @@
   }
 
   void _addExtensionMember(ExtensionMemberDescriptor extensionMember) {
-    final NamedNode replacement = extensionMember.member.node;
+    final NamedNode? replacement = extensionMember.member.node;
     if (replacement is! Member) return;
     Member member = replacement;
-    if (member.name.isPrivate && member.name.library != library) {
+    if (member.name!.isPrivate && member.name!.library != library) {
       // Members whose name is private to other libraries cannot currently
       // be found with the LibraryIndex class.
       return;
diff --git a/pkg/kernel/lib/src/assumptions.dart b/pkg/kernel/lib/src/assumptions.dart
index 00b07a9..1d96904 100644
--- a/pkg/kernel/lib/src/assumptions.dart
+++ b/pkg/kernel/lib/src/assumptions.dart
@@ -2,7 +2,6 @@
 // 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.
 
-// ignore: import_of_legacy_library_into_null_safe
 import '../ast.dart';
 
 /// Pairs of [TypeParameter]s that are currently assumed to be
diff --git a/pkg/kernel/lib/src/hierarchy_based_type_environment.dart b/pkg/kernel/lib/src/hierarchy_based_type_environment.dart
index 4a02f4e..58186dd 100644
--- a/pkg/kernel/lib/src/hierarchy_based_type_environment.dart
+++ b/pkg/kernel/lib/src/hierarchy_based_type_environment.dart
@@ -4,7 +4,6 @@
 
 library kernel.hierarchy_based_type_environment;
 
-// ignore: import_of_legacy_library_into_null_safe
 import '../ast.dart' show Class, DartType, InterfaceType, Library, Member, Name;
 
 import '../class_hierarchy.dart' show ClassHierarchyBase;
diff --git a/pkg/kernel/lib/src/legacy_erasure.dart b/pkg/kernel/lib/src/legacy_erasure.dart
index db2d148..316a701 100644
--- a/pkg/kernel/lib/src/legacy_erasure.dart
+++ b/pkg/kernel/lib/src/legacy_erasure.dart
@@ -2,9 +2,7 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE.md file.
 
-// ignore: import_of_legacy_library_into_null_safe
 import '../ast.dart';
-
 import 'replacement_visitor.dart';
 
 /// Returns legacy erasure of [type], that is, the type in which all nnbd
diff --git a/pkg/kernel/lib/src/merge_visitor.dart b/pkg/kernel/lib/src/merge_visitor.dart
index b6ec3f1..392c59a 100644
--- a/pkg/kernel/lib/src/merge_visitor.dart
+++ b/pkg/kernel/lib/src/merge_visitor.dart
@@ -2,7 +2,6 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE.md file.
 
-// ignore: import_of_legacy_library_into_null_safe
 import '../ast.dart';
 import '../type_algebra.dart';
 
@@ -73,13 +72,13 @@
 
       for (int i = 0; i < newTypeParameters.length; i++) {
         DartType? newBound =
-            mergeTypes(a.typeParameters[i].bound, b.typeParameters[i].bound);
+            mergeTypes(a.typeParameters[i].bound!, b.typeParameters[i].bound!);
         if (newBound == null) {
           return null;
         }
         newTypeParameters[i].bound = newBound;
         DartType? newDefaultType = mergeTypes(
-            a.typeParameters[i].defaultType, b.typeParameters[i].defaultType);
+            a.typeParameters[i].defaultType!, b.typeParameters[i].defaultType!);
         if (newDefaultType == null) {
           return null;
         }
@@ -90,7 +89,7 @@
     DartType? newReturnType = mergeTypes(a.returnType, b.returnType);
     if (newReturnType == null) return null;
     List<DartType> newPositionalParameters =
-        new List<DartType>.filled(a.positionalParameters.length, dartTypeDummy);
+        new List<DartType>.filled(a.positionalParameters.length, dummyDartType);
     for (int i = 0; i < a.positionalParameters.length; i++) {
       DartType? newType =
           mergeTypes(a.positionalParameters[i], b.positionalParameters[i]);
@@ -100,7 +99,7 @@
       newPositionalParameters[i] = newType;
     }
     List<NamedType> newNamedParameters =
-        new List<NamedType>.filled(a.namedParameters.length, namedTypeDummy);
+        new List<NamedType>.filled(a.namedParameters.length, dummyNamedType);
     for (int i = 0; i < a.namedParameters.length; i++) {
       DartType? newType =
           mergeTypes(a.namedParameters[i].type, b.namedParameters[i].type);
@@ -116,7 +115,8 @@
     }
     TypedefType? newTypedefType;
     if (a.typedefType != null && b.typedefType != null) {
-      newTypedefType = mergeTypes(a.typedefType, b.typedefType) as TypedefType?;
+      newTypedefType =
+          mergeTypes(a.typedefType!, b.typedefType!) as TypedefType?;
       // If the typedef couldn't be merged we just omit it from the resulting
       // function type since the typedef type is only informational.
     }
@@ -157,7 +157,7 @@
       return new InterfaceType(a.classNode, nullability);
     }
     List<DartType> newTypeArguments =
-        new List<DartType>.filled(a.typeArguments.length, dartTypeDummy);
+        new List<DartType>.filled(a.typeArguments.length, dummyDartType);
     for (int i = 0; i < a.typeArguments.length; i++) {
       DartType? newType = a.typeArguments[i].accept1(this, b.typeArguments[i]);
       if (newType == null) {
@@ -259,7 +259,8 @@
     assert(a.parameter == b.parameter);
     assert(a.promotedBound != null);
     assert(b.promotedBound != null);
-    DartType? newPromotedBound = a.promotedBound.accept1(this, b.promotedBound);
+    DartType? newPromotedBound =
+        a.promotedBound!.accept1(this, b.promotedBound);
     if (newPromotedBound == null) {
       return null;
     }
@@ -287,7 +288,7 @@
       return new TypedefType(a.typedefNode, nullability);
     }
     List<DartType> newTypeArguments =
-        new List<DartType>.filled(a.typeArguments.length, dartTypeDummy);
+        new List<DartType>.filled(a.typeArguments.length, dummyDartType);
     for (int i = 0; i < a.typeArguments.length; i++) {
       DartType? newType = a.typeArguments[i].accept1(this, b.typeArguments[i]);
       if (newType == null) return null;
diff --git a/pkg/kernel/lib/src/nnbd_top_merge.dart b/pkg/kernel/lib/src/nnbd_top_merge.dart
index 0116f5f..632ccc3 100644
--- a/pkg/kernel/lib/src/nnbd_top_merge.dart
+++ b/pkg/kernel/lib/src/nnbd_top_merge.dart
@@ -2,7 +2,6 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE.md file.
 
-// ignore: import_of_legacy_library_into_null_safe
 import '../ast.dart';
 import '../core_types.dart';
 
@@ -17,7 +16,7 @@
     return a;
   }
   List<DartType> newTypeArguments =
-      new List<DartType>.filled(a.typeArguments.length, dartTypeDummy);
+      new List<DartType>.filled(a.typeArguments.length, dummyDartType);
   for (int i = 0; i < a.typeArguments.length; i++) {
     DartType? newTypeArgument =
         nnbdTopMerge(coreTypes, a.typeArguments[i], b.typeArguments[i]);
diff --git a/pkg/kernel/lib/src/non_null.dart b/pkg/kernel/lib/src/non_null.dart
index c3df22a..2223d74 100644
--- a/pkg/kernel/lib/src/non_null.dart
+++ b/pkg/kernel/lib/src/non_null.dart
@@ -2,7 +2,6 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE.md file.
 
-// ignore: import_of_legacy_library_into_null_safe
 import '../ast.dart';
 
 /// Returns the type defines as `NonNull(type)` in the nnbd specification.
@@ -78,12 +77,12 @@
       return null;
     }
     if (node.promotedBound != null) {
-      if (node.promotedBound.nullability == Nullability.nonNullable) {
+      if (node.promotedBound!.nullability == Nullability.nonNullable) {
         // The promoted bound is already non-nullable so we set the declared
         // nullability to non-nullable.
         return node.withDeclaredNullability(Nullability.nonNullable);
       }
-      DartType? promotedBound = node.promotedBound.accept(this);
+      DartType? promotedBound = node.promotedBound!.accept(this);
       if (promotedBound == null) {
         // The promoted bound could not be made non-nullable so we set the
         // declared nullability to undetermined.
@@ -91,7 +90,7 @@
           return null;
         }
         return new TypeParameterType.intersection(
-            node.parameter, Nullability.undetermined, node.promotedBound);
+            node.parameter, Nullability.undetermined, node.promotedBound!);
       } else if (promotedBound.nullability == Nullability.nonNullable) {
         // The bound could be made non-nullable so we use it as the promoted
         // bound.
diff --git a/pkg/kernel/lib/src/norm.dart b/pkg/kernel/lib/src/norm.dart
index c9f004c..78ab826 100644
--- a/pkg/kernel/lib/src/norm.dart
+++ b/pkg/kernel/lib/src/norm.dart
@@ -2,7 +2,6 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE.md file.
 
-// ignore: import_of_legacy_library_into_null_safe
 import '../ast.dart';
 import '../core_types.dart';
 import '../type_algebra.dart';
@@ -94,7 +93,7 @@
   @override
   DartType? visitTypeParameterType(TypeParameterType node, int variance) {
     if (node.promotedBound == null) {
-      DartType bound = node.parameter.bound;
+      DartType bound = node.parameter.bound!;
       if (normalizesToNever(bound)) {
         DartType result = NeverType.fromNullability(node.nullability);
         return result.accept1(this, variance) ?? result;
@@ -103,7 +102,7 @@
       // If the bound isn't Never, the type is already normalized.
       return null;
     } else {
-      DartType bound = node.promotedBound;
+      DartType bound = node.promotedBound!;
       bound = bound.accept1(this, variance) ?? bound;
       if (bound is NeverType && bound.nullability == Nullability.nonNullable) {
         return bound;
@@ -119,7 +118,7 @@
         assert(!coreTypes.isTop(bound));
         return new TypeParameterType(node.parameter, node.declaredNullability);
       } else if (bound == coreTypes.objectNonNullableRawType &&
-          norm(coreTypes, node.parameter.bound) ==
+          norm(coreTypes, node.parameter.bound!) ==
               coreTypes.objectNonNullableRawType) {
         return new TypeParameterType(node.parameter, node.declaredNullability);
       } else if (identical(bound, node.promotedBound)) {
@@ -144,9 +143,9 @@
       return true;
     } else if (type is TypeParameterType) {
       if (type.promotedBound == null) {
-        return normalizesToNever(type.parameter.bound);
+        return normalizesToNever(type.parameter.bound!);
       } else {
-        return normalizesToNever(type.promotedBound);
+        return normalizesToNever(type.promotedBound!);
       }
     }
     return false;
diff --git a/pkg/kernel/lib/src/printer.dart b/pkg/kernel/lib/src/printer.dart
index d14c8cd..5874ca7 100644
--- a/pkg/kernel/lib/src/printer.dart
+++ b/pkg/kernel/lib/src/printer.dart
@@ -2,7 +2,6 @@
 // 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.
 
-// ignore: import_of_legacy_library_into_null_safe
 import '../ast.dart';
 import 'text_util.dart';
 
@@ -157,8 +156,9 @@
   }
 
   String getVariableName(VariableDeclaration node) {
-    if (node.name != null) {
-      return node.name;
+    String? name = node.name;
+    if (name != null) {
+      return name;
     }
     return _variableNames[node] ??= '#${_variableNames.length}';
   }
@@ -259,22 +259,22 @@
       for (TypeParameter typeParameter in typeParameters) {
         _sb.write(comma);
         _sb.write(typeParameter.name);
-        DartType bound = typeParameter.bound;
+        DartType bound = typeParameter.bound!;
 
         bool isTopObject(DartType type) {
           if (type is InterfaceType &&
               type.className.node != null &&
               type.classNode.name == 'Object') {
-            Uri? uri = type.classNode.enclosingLibrary?.importUri;
-            return uri?.scheme == 'dart' &&
-                uri?.path == 'core' &&
+            Uri uri = type.classNode.enclosingLibrary.importUri;
+            return uri.scheme == 'dart' &&
+                uri.path == 'core' &&
                 (type.nullability == Nullability.legacy ||
                     type.nullability == Nullability.nullable);
           }
           return false;
         }
 
-        if (!isTopObject(bound) || isTopObject(typeParameter.defaultType)) {
+        if (!isTopObject(bound) || isTopObject(typeParameter.defaultType!)) {
           // Include explicit bounds only.
           _sb.write(' extends ');
           writeType(bound);
@@ -385,7 +385,7 @@
     _sb.write(getVariableName(node));
     if (includeInitializer && node.initializer != null && !node.isRequired) {
       _sb.write(' = ');
-      writeExpression(node.initializer);
+      writeExpression(node.initializer!);
     }
   }
 
@@ -401,7 +401,7 @@
         }
         _sb.write(node.typeParameters[index].name);
         _sb.write(' extends ');
-        writeType(node.typeParameters[index].bound);
+        writeType(node.typeParameters[index].bound!);
       }
       _sb.write('>');
     }
@@ -437,7 +437,7 @@
     if (body != null) {
       if (body is ReturnStatement) {
         _sb.write(' => ');
-        writeExpression(body.expression);
+        writeExpression(body.expression!);
       } else {
         _sb.write(' ');
         writeStatement(body);
diff --git a/pkg/kernel/lib/src/replacement_visitor.dart b/pkg/kernel/lib/src/replacement_visitor.dart
index ed4301c..8fbce4a 100644
--- a/pkg/kernel/lib/src/replacement_visitor.dart
+++ b/pkg/kernel/lib/src/replacement_visitor.dart
@@ -2,7 +2,6 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE.md file.
 
-// ignore: import_of_legacy_library_into_null_safe
 import '../ast.dart';
 import '../type_algebra.dart';
 
@@ -49,7 +48,7 @@
           Substitution.fromPairs(node.typeParameters, typeParameterTypes);
       for (int i = 0; i < newTypeParameters.length; i++) {
         newTypeParameters[i].bound =
-            substitution.substituteType(newTypeParameters[i].bound);
+            substitution.substituteType(newTypeParameters[i].bound!);
       }
     }
 
@@ -211,7 +210,7 @@
   DartType? visitTypeParameterType(TypeParameterType node, int variance) {
     Nullability? newNullability = visitNullability(node);
     if (node.promotedBound != null) {
-      DartType newPromotedBound = node.promotedBound.accept1(this, variance);
+      DartType? newPromotedBound = node.promotedBound!.accept1(this, variance);
       return createPromotedTypeParameterType(
           node, newNullability, newPromotedBound);
     }
diff --git a/pkg/kernel/lib/src/standard_bounds.dart b/pkg/kernel/lib/src/standard_bounds.dart
index 834690f..dba4d98 100644
--- a/pkg/kernel/lib/src/standard_bounds.dart
+++ b/pkg/kernel/lib/src/standard_bounds.dart
@@ -4,7 +4,6 @@
 
 import 'dart:math' as math;
 
-// ignore: import_of_legacy_library_into_null_safe
 import '../ast.dart';
 import '../class_hierarchy.dart';
 import '../core_types.dart';
@@ -216,7 +215,7 @@
         s.promotedBound != null &&
         t is TypeParameterType &&
         t.promotedBound != null) {
-      return morebottom(s.promotedBound, t.promotedBound);
+      return morebottom(s.promotedBound!, t.promotedBound!);
     }
 
     // MOREBOTTOM(X&S, T) = true.
@@ -233,7 +232,7 @@
     if (s is TypeParameterType && t is TypeParameterType) {
       assert(s.promotedBound == null);
       assert(t.promotedBound == null);
-      return morebottom(s.parameter.bound, t.parameter.bound);
+      return morebottom(s.parameter.bound!, t.parameter.bound!);
     }
 
     throw new UnsupportedError("morebottom($s, $t)");
@@ -950,8 +949,8 @@
           // TODO(dmitryas): Figure out if a procedure for syntactic equality
           // should be used instead.
           if (!areMutualSubtypes(
-              f.typeParameters[i].bound,
-              substitution.substituteType(g.typeParameters[i].bound),
+              f.typeParameters[i].bound!,
+              substitution.substituteType(g.typeParameters[i].bound!),
               SubtypeCheckMode.withNullabilities)) {
             boundsMatch = false;
           }
@@ -967,7 +966,7 @@
     List<TypeParameter> typeParameters = f.typeParameters;
 
     List<DartType> positionalParameters =
-        new List<DartType>.filled(maxPos, dartTypeDummy);
+        new List<DartType>.filled(maxPos, dummyDartType);
     for (int i = 0; i < minPos; ++i) {
       positionalParameters[i] = _getNullabilityAwareStandardUpperBound(
           f.positionalParameters[i],
@@ -1149,8 +1148,8 @@
           // TODO(dmitryas): Figure out if a procedure for syntactic
           // equality should be used instead.
           if (!areMutualSubtypes(
-              f.typeParameters[i].bound,
-              substitution.substituteType(g.typeParameters[i].bound),
+              f.typeParameters[i].bound!,
+              substitution.substituteType(g.typeParameters[i].bound!),
               SubtypeCheckMode.withNullabilities)) {
             boundsMatch = false;
           }
@@ -1164,7 +1163,7 @@
     List<TypeParameter> typeParameters = f.typeParameters;
 
     List<DartType> positionalParameters =
-        new List<DartType>.filled(minPos, dartTypeDummy);
+        new List<DartType>.filled(minPos, dummyDartType);
     for (int i = 0; i < minPos; ++i) {
       positionalParameters[i] = _getNullabilityAwareStandardLowerBound(
           f.positionalParameters[i],
@@ -1233,12 +1232,12 @@
               topFunctionType: coreTypes.functionNonNullableRawType,
               unhandledTypeHandler: (type, recursor) => false);
       return _getNullabilityAwareStandardUpperBound(
-              eliminator.eliminateToGreatest(type1.parameter.bound),
+              eliminator.eliminateToGreatest(type1.parameter.bound!),
               type2,
               clientLibrary)
           .withDeclaredNullability(uniteNullabilities(
               type1.declaredNullability,
-              uniteNullabilities(type1.parameter.bound.declaredNullability,
+              uniteNullabilities(type1.parameter.bound!.declaredNullability,
                   type2.declaredNullability)));
     } else {
       // UP(X1 & B1, T2) =
@@ -1265,11 +1264,11 @@
               topFunctionType: coreTypes.functionNonNullableRawType,
               unhandledTypeHandler: (type, recursor) => false);
       return _getNullabilityAwareStandardUpperBound(
-              eliminator.eliminateToGreatest(type1.promotedBound),
+              eliminator.eliminateToGreatest(type1.promotedBound!),
               type2,
               clientLibrary)
           .withDeclaredNullability(uniteNullabilities(
-              type1.promotedBound.declaredNullability,
+              type1.promotedBound!.declaredNullability,
               type2.declaredNullability));
     }
   }
@@ -1419,7 +1418,7 @@
     int totalPositional =
         math.max(f.positionalParameters.length, g.positionalParameters.length);
     List<DartType> positionalParameters =
-        new List<DartType>.filled(totalPositional, dartTypeDummy);
+        new List<DartType>.filled(totalPositional, dummyDartType);
     for (int i = 0; i < totalPositional; i++) {
       if (i < f.positionalParameters.length) {
         DartType fType = f.positionalParameters[i];
@@ -1523,7 +1522,7 @@
     int totalPositional =
         math.min(f.positionalParameters.length, g.positionalParameters.length);
     List<DartType> positionalParameters =
-        new List<DartType>.filled(totalPositional, dartTypeDummy);
+        new List<DartType>.filled(totalPositional, dummyDartType);
     for (int i = 0; i < totalPositional; i++) {
       positionalParameters[i] = getStandardLowerBound(
           f.positionalParameters[i], g.positionalParameters[i], clientLibrary);
@@ -1602,7 +1601,7 @@
 
       assert(tArgs1.length == tArgs2.length);
       assert(tArgs1.length == tParams.length);
-      List<DartType> tArgs = new List.filled(tArgs1.length, dartTypeDummy);
+      List<DartType> tArgs = new List.filled(tArgs1.length, dummyDartType);
       for (int i = 0; i < tArgs1.length; i++) {
         if (tParams[i].variance == Variance.contravariant) {
           tArgs[i] = getStandardLowerBound(tArgs1[i], tArgs2[i], clientLibrary);
@@ -1675,14 +1674,14 @@
       // we need to replicate that behavior?
       return getStandardUpperBound(
           Substitution.fromMap({type1.parameter: coreTypes.objectLegacyRawType})
-              .substituteType(type1.parameter.bound),
+              .substituteType(type1.parameter.bound!),
           type2,
           clientLibrary);
     } else if (type2 is TypeParameterType) {
       return getStandardUpperBound(
           type1,
           Substitution.fromMap({type2.parameter: coreTypes.objectLegacyRawType})
-              .substituteType(type2.parameter.bound),
+              .substituteType(type2.parameter.bound!),
           clientLibrary);
     } else {
       // We should only be called when at least one of the types is a
diff --git a/pkg/kernel/lib/src/text_util.dart b/pkg/kernel/lib/src/text_util.dart
index 0e9e0cc..873fafb 100644
--- a/pkg/kernel/lib/src/text_util.dart
+++ b/pkg/kernel/lib/src/text_util.dart
@@ -2,7 +2,6 @@
 // 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.
 
-// ignore: import_of_legacy_library_into_null_safe
 import '../ast.dart';
 
 String nullabilityToString(Nullability nullability) {
@@ -47,17 +46,17 @@
     {bool includeLibraryName: false}) {
   if (canonicalName.isRoot) {
     return '<root>';
-  } else if (canonicalName.parent.isRoot) {
+  } else if (canonicalName.parent!.isRoot) {
     return canonicalName.name;
-  } else if (canonicalName.parent.parent.isRoot) {
+  } else if (canonicalName.parent!.parent!.isRoot) {
     if (!includeLibraryName) {
       return canonicalName.name;
     }
-    String parentName = qualifiedCanonicalNameToString(canonicalName.parent,
+    String parentName = qualifiedCanonicalNameToString(canonicalName.parent!,
         includeLibraryName: includeLibraryName);
     return '$parentName::${canonicalName.name}';
   } else {
-    String parentName = qualifiedCanonicalNameToString(canonicalName.parent,
+    String parentName = qualifiedCanonicalNameToString(canonicalName.parent!,
         includeLibraryName: includeLibraryName);
     return '$parentName.${canonicalName.name}';
   }
@@ -165,15 +164,13 @@
 }
 
 String typedefNameToString(Typedef? node) {
-  return node == null
-      ? 'null'
-      : node.name ?? 'null-named typedef ${node.runtimeType} ${node.hashCode}';
+  return node == null ? 'null' : node.name;
 }
 
 String qualifiedMemberNameToString(Member node,
     {bool includeLibraryName: false}) {
   if (node.enclosingClass != null) {
-    return qualifiedClassNameToString(node.enclosingClass,
+    return qualifiedClassNameToString(node.enclosingClass!,
             includeLibraryName: includeLibraryName) +
         '.' +
         memberNameToString(node);
@@ -215,7 +212,7 @@
 
 String qualifiedTypeParameterNameToString(TypeParameter node,
     {bool includeLibraryName: false}) {
-  TreeNode parent = node.parent;
+  TreeNode? parent = node.parent;
   if (parent is Class) {
     return qualifiedClassNameToString(parent,
             includeLibraryName: includeLibraryName) +
diff --git a/pkg/kernel/lib/src/types.dart b/pkg/kernel/lib/src/types.dart
index b54c79d..119100c 100644
--- a/pkg/kernel/lib/src/types.dart
+++ b/pkg/kernel/lib/src/types.dart
@@ -2,7 +2,6 @@
 // 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.
 
-// ignore: import_of_legacy_library_into_null_safe
 import '../ast.dart'
     show
         BottomType,
@@ -381,7 +380,7 @@
   IsSubtypeOf isTypeParameterRelated(
       TypeParameterType s, InterfaceType t, Types types) {
     return types
-        .performNullabilityAwareSubtypeCheck(s.parameter.bound, t)
+        .performNullabilityAwareSubtypeCheck(s.parameter.bound!, t)
         .and(new IsSubtypeOf.basedSolelyOnNullabilities(s, t));
   }
 
@@ -402,7 +401,7 @@
   IsSubtypeOf isIntersectionRelated(
       TypeParameterType intersection, InterfaceType t, Types types) {
     return types.performNullabilityAwareSubtypeCheck(
-        intersection.promotedBound, t); // Rule 12.
+        intersection.promotedBound!, t); // Rule 12.
   }
 
   @override
@@ -454,7 +453,7 @@
         TypeParameter sTypeVariable = sTypeVariables[i];
         TypeParameter tTypeVariable = tTypeVariables[i];
         result = result.and(types.performNullabilityAwareMutualSubtypesCheck(
-            sTypeVariable.bound, tTypeVariable.bound));
+            sTypeVariable.bound!, tTypeVariable.bound!));
         typeVariableSubstitution.add(new TypeParameterType.forAlphaRenaming(
             sTypeVariable, tTypeVariable));
       }
@@ -468,8 +467,8 @@
           TypeParameter sTypeVariable = sTypeVariables[i];
           TypeParameter tTypeVariable = tTypeVariables[i];
           result = result.and(types.performNullabilityAwareMutualSubtypesCheck(
-              substitution.substituteType(sTypeVariable.bound),
-              tTypeVariable.bound));
+              substitution.substituteType(sTypeVariable.bound!),
+              tTypeVariable.bound!));
           if (!result.isSubtypeWhenIgnoringNullabilities()) {
             return const IsSubtypeOf.never();
           }
@@ -582,7 +581,7 @@
       TypeParameterType intersection, FunctionType t, Types types) {
     // Rule 12.
     return types.performNullabilityAwareSubtypeCheck(
-        intersection.promotedBound, t);
+        intersection.promotedBound!, t);
   }
 
   @override
@@ -590,7 +589,7 @@
       TypeParameterType s, FunctionType t, Types types) {
     // Rule 13.
     return types
-        .performNullabilityAwareSubtypeCheck(s.parameter.bound, t)
+        .performNullabilityAwareSubtypeCheck(s.parameter.bound!, t)
         .and(new IsSubtypeOf.basedSolelyOnNullabilities(s, t));
   }
 
@@ -645,7 +644,7 @@
 
     // Rule 12.
     return types.performNullabilityAwareSubtypeCheck(
-        intersection.promotedBound
+        intersection.promotedBound!
             .withDeclaredNullability(intersection.nullability),
         t);
   }
@@ -826,9 +825,9 @@
             s, t.typeArgument.withDeclaredNullability(t.nullability))
         // Rule 13.
         .orSubtypeCheckFor(
-            s.parameter.bound.withDeclaredNullability(
+            s.parameter.bound!.withDeclaredNullability(
                 combineNullabilitiesForSubstitution(
-                    s.parameter.bound.nullability, s.nullability)),
+                    s.parameter.bound!.nullability, s.nullability)),
             t,
             types)
         // Rule 10.
@@ -850,7 +849,7 @@
   IsSubtypeOf isIntersectionRelated(
       TypeParameterType intersection, FutureOrType t, Types types) {
     return isTypeParameterRelated(intersection, t, types) // Rule 8.
-        .orSubtypeCheckFor(intersection.promotedBound, t, types); // Rule 12.
+        .orSubtypeCheckFor(intersection.promotedBound!, t, types); // Rule 12.
   }
 
   @override
@@ -868,7 +867,7 @@
     // Rule 9.
     return const IsTypeParameterSubtypeOf()
         .isIntersectionRelated(sIntersection, tIntersection, types)
-        .andSubtypeCheckFor(sIntersection, tIntersection.promotedBound, types);
+        .andSubtypeCheckFor(sIntersection, tIntersection.promotedBound!, types);
   }
 
   @override
@@ -877,7 +876,7 @@
     // Rule 9.
     return const IsTypeParameterSubtypeOf()
         .isTypeParameterRelated(s, intersection, types)
-        .andSubtypeCheckFor(s, intersection.promotedBound, types);
+        .andSubtypeCheckFor(s, intersection.promotedBound!, types);
   }
 
   @override
@@ -936,7 +935,7 @@
   IsSubtypeOf isIntersectionRelated(
       TypeParameterType intersection, NullType t, Types types) {
     return types.performNullabilityAwareMutualSubtypesCheck(
-        intersection.promotedBound, t);
+        intersection.promotedBound!, t);
   }
 
   IsSubtypeOf isFunctionRelated(FunctionType s, NullType t, Types types) {
@@ -978,7 +977,7 @@
   IsSubtypeOf isIntersectionRelated(
       TypeParameterType intersection, NeverType t, Types types) {
     return types.performNullabilityAwareSubtypeCheck(
-        intersection.promotedBound, t);
+        intersection.promotedBound!, t);
   }
 
   IsSubtypeOf isFunctionRelated(FunctionType s, NeverType t, Types types) {
diff --git a/pkg/kernel/lib/text/ast_to_text.dart b/pkg/kernel/lib/text/ast_to_text.dart
index 26699f0..871f92b 100644
--- a/pkg/kernel/lib/text/ast_to_text.dart
+++ b/pkg/kernel/lib/text/ast_to_text.dart
@@ -2,8 +2,6 @@
 // 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.
 
-// @dart = 2.9
-
 library kernel.ast_to_text;
 
 import 'dart:core' hide MapEntry;
@@ -26,11 +24,13 @@
 
 class NormalNamer<T> extends Namer<T> {
   final String prefix;
+
   NormalNamer(this.prefix);
 }
 
 class ConstantNamer extends RecursiveResultVisitor<Null> with Namer<Constant> {
   final String prefix;
+
   ConstantNamer(this.prefix);
 
   String getName(Constant constant) {
@@ -78,7 +78,7 @@
   final Map<U, String> namesU = <U, String>{};
   final Set<String> usedNames = new Set<String>();
 
-  String disambiguate(T key1, U key2, String proposeName()) {
+  String disambiguate(T? key1, U? key2, String proposeName()) {
     String getNewName() {
       String proposedName = proposeName();
       if (usedNames.add(proposedName)) return proposedName;
@@ -90,12 +90,12 @@
     }
 
     if (key1 != null) {
-      String result = namesT[key1];
+      String? result = namesT[key1];
       if (result != null) return result;
       return namesT[key1] = getNewName();
     }
     if (key2 != null) {
-      String result = namesU[key2];
+      String? result = namesU[key2];
       if (result != null) return result;
       return namesU[key2] = getNewName();
     }
@@ -105,13 +105,13 @@
 
 NameSystem globalDebuggingNames = new NameSystem();
 
-String debugLibraryName(Library node) {
+String debugLibraryName(Library? node) {
   return node == null
       ? 'null'
       : node.name ?? globalDebuggingNames.nameLibrary(node);
 }
 
-String debugClassName(Class node) {
+String debugClassName(Class? node) {
   return node == null
       ? 'null'
       : node.name ?? globalDebuggingNames.nameClass(node);
@@ -127,7 +127,7 @@
 
 String debugQualifiedMemberName(Member node) {
   if (node.enclosingClass != null) {
-    return debugQualifiedClassName(node.enclosingClass) +
+    return debugQualifiedClassName(node.enclosingClass!) +
         '::' +
         debugMemberName(node);
   } else {
@@ -142,13 +142,14 @@
 }
 
 String debugQualifiedTypeParameterName(TypeParameter node) {
-  if (node.parent is Class) {
-    return debugQualifiedClassName(node.parent) +
+  TreeNode? parent = node.parent;
+  if (parent is Class) {
+    return debugQualifiedClassName(parent) +
         '::' +
         debugTypeParameterName(node);
   }
-  if (node.parent is Member) {
-    return debugQualifiedMemberName(node.parent) +
+  if (parent is Member) {
+    return debugQualifiedMemberName(parent) +
         '::' +
         debugTypeParameterName(node);
   }
@@ -212,11 +213,17 @@
 
   final RegExp pathSeparator = new RegExp('[\\/]');
 
-  String nameLibraryPrefix(Library node, {String proposedName}) {
+  String nameLibraryPrefix(Library node, {String? proposedName}) {
     return prefixes.disambiguate(node.reference, node.reference.canonicalName,
         () {
-      if (proposedName != null) return proposedName;
-      if (node.name != null) return abbreviateName(node.name);
+      if (proposedName != null) {
+        return proposedName;
+      }
+      String? name = node.name;
+      if (name != null) {
+        return abbreviateName(name);
+      }
+      // ignore: unnecessary_null_comparison
       if (node.importUri != null) {
         String path = node.importUri.hasEmptyPath
             ? '${node.importUri}'
@@ -230,13 +237,13 @@
     });
   }
 
-  nameCanonicalNameAsLibraryPrefix(Reference node, CanonicalName name,
-      {String proposedName}) {
+  nameCanonicalNameAsLibraryPrefix(Reference? node, CanonicalName? name,
+      {String? proposedName}) {
     return prefixes.disambiguate(node, name, () {
       if (proposedName != null) return proposedName;
-      CanonicalName canonicalName = name ?? node.canonicalName;
+      CanonicalName? canonicalName = name ?? node?.canonicalName;
       if (canonicalName?.name != null) {
-        String path = canonicalName.name;
+        String path = canonicalName!.name;
         int slash = path.lastIndexOf(pathSeparator);
         if (slash >= 0) {
           path = path.substring(slash + 1);
@@ -274,9 +281,9 @@
 class Printer extends Visitor<void> with VisitorVoidMixin {
   final NameSystem syntheticNames;
   final StringSink sink;
-  final Annotator annotator;
-  final Map<String, MetadataRepository<Object>> metadata;
-  ImportTable importTable;
+  final Annotator? annotator;
+  final Map<String, MetadataRepository<dynamic>>? metadata;
+  ImportTable? importTable;
   int indentation = 0;
   int column = 0;
   bool showOffsets;
@@ -288,7 +295,7 @@
   int state = SPACE;
 
   Printer(this.sink,
-      {NameSystem syntheticNames,
+      {NameSystem? syntheticNames,
       this.showOffsets: false,
       this.showMetadata: false,
       this.importTable,
@@ -297,7 +304,7 @@
       : this.syntheticNames = syntheticNames ?? new NameSystem();
 
   Printer createInner(ImportTable importTable,
-      Map<String, MetadataRepository<Object>> metadata) {
+      Map<String, MetadataRepository<dynamic>>? metadata) {
     return new Printer(sink,
         importTable: importTable,
         metadata: metadata,
@@ -319,8 +326,9 @@
   }
 
   String getLibraryReference(Library node) {
+    // ignore: unnecessary_null_comparison
     if (node == null) return '<No Library>';
-    if (importTable != null && importTable.getImportIndex(node) != -1) {
+    if (importTable != null && importTable?.getImportIndex(node) != -1) {
       return syntheticNames.nameLibraryPrefix(node);
     }
     return getLibraryName(node);
@@ -335,6 +343,7 @@
   }
 
   String getClassReference(Class node) {
+    // ignore: unnecessary_null_comparison
     if (node == null) return '<No Class>';
     String name = getClassName(node);
     String library = getLibraryReference(node.enclosingLibrary);
@@ -342,6 +351,7 @@
   }
 
   String getTypedefReference(Typedef node) {
+    // ignore: unnecessary_null_comparison
     if (node == null) return '<No Typedef>';
     String library = getLibraryReference(node.enclosingLibrary);
     return '$library::${node.name}';
@@ -352,15 +362,17 @@
 
   Name getMemberName(Member node) {
     if (node.name?.text == '') return emptyName;
-    if (node.name != null) return node.name;
+    if (node.name != null) return node.name!;
     return new Name(syntheticNames.nameMember(node));
   }
 
   String getMemberReference(Member node) {
+    // ignore: unnecessary_null_comparison
     if (node == null) return '<No Member>';
     String name = getMemberName(node).text;
-    if (node.parent is Class) {
-      String className = getClassReference(node.parent);
+    Class? enclosingClass = node.enclosingClass;
+    if (enclosingClass != null) {
+      String className = getClassReference(enclosingClass);
       return '$className::$name';
     } else {
       String library = getLibraryReference(node.enclosingLibrary);
@@ -373,6 +385,7 @@
   }
 
   String getVariableReference(VariableDeclaration node) {
+    // ignore: unnecessary_null_comparison
     if (node == null) return '<No VariableDeclaration>';
     return getVariableName(node);
   }
@@ -382,13 +395,15 @@
   }
 
   String getTypeParameterReference(TypeParameter node) {
+    // ignore: unnecessary_null_comparison
     if (node == null) return '<No TypeParameter>';
     String name = getTypeParameterName(node);
-    if (node.parent is FunctionNode && node.parent.parent is Member) {
-      String member = getMemberReference(node.parent.parent);
+    TreeNode? parent = node.parent;
+    if (parent is FunctionNode && parent.parent is Member) {
+      String member = getMemberReference(parent.parent as Member);
       return '$member::$name';
-    } else if (node.parent is Class) {
-      String className = getClassReference(node.parent);
+    } else if (parent is Class) {
+      String className = getClassReference(parent);
       return '$className::$name';
     } else {
       return name; // Bound inside a function type.
@@ -399,8 +414,8 @@
     writeProblemsAsJson("Problems in component", component.problemsAsJson);
   }
 
-  void writeProblemsAsJson(String header, List<String> problemsAsJson) {
-    if (problemsAsJson?.isEmpty == false) {
+  void writeProblemsAsJson(String header, List<String>? problemsAsJson) {
+    if (problemsAsJson != null && problemsAsJson.isNotEmpty) {
       endLine("//");
       write("// ");
       write(header);
@@ -408,7 +423,8 @@
       endLine("//");
       for (String s in problemsAsJson) {
         Map<String, Object> decoded = json.decode(s);
-        List<Object> plainTextFormatted = decoded["plainTextFormatted"];
+        List<Object> plainTextFormatted =
+            decoded["plainTextFormatted"] as List<Object>;
         List<String> lines = plainTextFormatted.join("\n").split("\n");
         for (int i = 0; i < lines.length; i++) {
           write("//");
@@ -424,8 +440,9 @@
   void writeLibraryFile(Library library) {
     writeAnnotationList(library.annotations);
     writeWord('library');
-    if (library.name != null) {
-      writeWord(library.name);
+    String? name = library.name;
+    if (name != null) {
+      writeWord(name);
     }
     if (library.isNonNullableByDefault) {
       writeWord("/*isNonNullableByDefault*/");
@@ -453,7 +470,7 @@
   }
 
   void writeStandardLibraryContent(Library library,
-      {Printer outerPrinter, LibraryImportTable importsToPrint}) {
+      {Printer? outerPrinter, LibraryImportTable? importsToPrint}) {
     outerPrinter ??= this;
     outerPrinter.writeProblemsAsJson(
         "Problems in library", library.problemsAsJson);
@@ -479,23 +496,23 @@
     write('additionalExports = (');
     for (int i = 0; i < additionalExports.length; i++) {
       Reference reference = additionalExports[i];
-      NamedNode node = reference.node;
+      NamedNode? node = reference.node;
       if (node is Class) {
         Library nodeLibrary = node.enclosingLibrary;
         String prefix = syntheticNames.nameLibraryPrefix(nodeLibrary);
-        write(prefix + '::' + node.name);
+        write(prefix + '::' + node.name!);
       } else if (node is Extension) {
         Library nodeLibrary = node.enclosingLibrary;
         String prefix = syntheticNames.nameLibraryPrefix(nodeLibrary);
-        write(prefix + '::' + node.name);
+        write(prefix + '::' + node.name!);
       } else if (node is Field) {
         Library nodeLibrary = node.enclosingLibrary;
         String prefix = syntheticNames.nameLibraryPrefix(nodeLibrary);
-        write(prefix + '::' + node.name.text);
+        write(prefix + '::' + node.name!.text);
       } else if (node is Procedure) {
         Library nodeLibrary = node.enclosingLibrary;
         String prefix = syntheticNames.nameLibraryPrefix(nodeLibrary);
-        write(prefix + '::' + node.name.text);
+        write(prefix + '::' + node.name!.text);
       } else if (node is Typedef) {
         Library nodeLibrary = node.enclosingLibrary;
         String prefix = syntheticNames.nameLibraryPrefix(nodeLibrary);
@@ -532,9 +549,11 @@
       }
       writeAnnotationList(library.annotations);
       writeWord('library');
-      if (library.name != null) {
-        writeWord(library.name);
+      String? name = library.name;
+      if (name != null) {
+        writeWord(name);
       }
+      // ignore: unnecessary_null_comparison
       if (library.importUri != null) {
         writeSpaced('from');
         writeWord('"${library.importUri}"');
@@ -567,7 +586,7 @@
     endLine('}');
   }
 
-  int getPrecedence(TreeNode node) {
+  int getPrecedence(Expression node) {
     return Precedence.of(node);
   }
 
@@ -618,7 +637,7 @@
     writeSpace('  ' * indentation);
   }
 
-  void writeNode(Node node) {
+  void writeNode(Node? node) {
     if (node == null) {
       writeSymbol("<Null>");
     } else {
@@ -642,16 +661,10 @@
     }
   }
 
-  void writeOptionalNode(Node node) {
-    if (node != null) {
-      node.accept(this);
-    }
-  }
-
   void writeMetadata(TreeNode node) {
     if (metadata != null) {
-      for (MetadataRepository<Object> md in metadata.values) {
-        final Object nodeMetadata = md.mapping[node];
+      for (MetadataRepository<dynamic> md in metadata!.values) {
+        final dynamic nodeMetadata = md.mapping[node];
         if (nodeMetadata != null) {
           writeWord("[@${md.tag}=${nodeMetadata}]");
         }
@@ -659,7 +672,7 @@
     }
   }
 
-  void writeAnnotatedType(DartType type, String annotation) {
+  void writeAnnotatedType(DartType type, String? annotation) {
     writeType(type);
     if (annotation != null) {
       write('/');
@@ -669,6 +682,7 @@
   }
 
   void writeType(DartType type) {
+    // ignore: unnecessary_null_comparison
     if (type == null) {
       write('<No DartType>');
     } else {
@@ -677,12 +691,14 @@
   }
 
   void writeOptionalType(DartType type) {
+    // ignore: unnecessary_null_comparison
     if (type != null) {
       type.accept(this);
     }
   }
 
   visitSupertype(Supertype type) {
+    // ignore: unnecessary_null_comparison
     if (type == null) {
       write('<No Supertype>');
     } else {
@@ -711,14 +727,14 @@
   }
 
   void writeName(Name name) {
-    if (name?.text == '') {
+    if (name.text == '') {
       writeWord(emptyNameString);
     } else {
-      writeWord(name?.text ?? '<anonymous>'); // TODO: write library name
+      writeWord(name.text); // TODO: write library name
     }
   }
 
-  void endLine([String string]) {
+  void endLine([String? string]) {
     if (string != null) {
       write(string);
     }
@@ -728,7 +744,7 @@
   }
 
   void writeFunction(FunctionNode function,
-      {name, List<Initializer> initializers, bool terminateLine: true}) {
+      {name, List<Initializer>? initializers, bool terminateLine: true}) {
     if (name is String) {
       writeWord(name);
     } else if (name is Name) {
@@ -758,8 +774,9 @@
       writeSpaced(getAsyncMarkerKeyword(function.dartAsyncMarker));
       writeSpaced("*/");
     }
-    if (function.body != null) {
-      writeFunctionBody(function.body, terminateLine: terminateLine);
+    Statement? body = function.body;
+    if (body != null) {
+      writeFunctionBody(body, terminateLine: terminateLine);
     } else if (terminateLine) {
       endLine(';');
     } else {
@@ -806,15 +823,15 @@
       }
     } else if (body is ReturnStatement && !terminateLine) {
       writeSpaced('=>');
-      writeExpression(body.expression);
+      writeExpression(body.expression!);
     } else {
       writeBody(body);
     }
   }
 
   writeFunctionType(FunctionType node,
-      {List<VariableDeclaration> typedefPositional,
-      List<VariableDeclaration> typedefNamed}) {
+      {List<VariableDeclaration>? typedefPositional,
+      List<VariableDeclaration>? typedefNamed}) {
     if (state == WORD) {
       ensureSpace();
     }
@@ -892,7 +909,8 @@
     }
   }
 
-  void writeReturnType(DartType type, String annotation) {
+  void writeReturnType(DartType type, String? annotation) {
+    // ignore: unnecessary_null_comparison
     if (type == null) return;
     writeSpaced('→');
     writeAnnotatedType(type, annotation);
@@ -949,23 +967,24 @@
   }
 
   String getClassReferenceFromReference(Reference reference) {
+    // ignore: unnecessary_null_comparison
     if (reference == null) return '<No Class>';
     if (reference.node != null) return getClassReference(reference.asClass);
     if (reference.canonicalName != null) {
-      return getCanonicalNameString(reference.canonicalName);
+      return getCanonicalNameString(reference.canonicalName!);
     }
     throw "Neither node nor canonical name found";
   }
 
-  void writeMemberReferenceFromReference(Reference reference) {
+  void writeMemberReferenceFromReference(Reference? reference) {
     writeWord(getMemberReferenceFromReference(reference));
   }
 
-  String getMemberReferenceFromReference(Reference reference) {
+  String getMemberReferenceFromReference(Reference? reference) {
     if (reference == null) return '<No Member>';
     if (reference.node != null) return getMemberReference(reference.asMember);
     if (reference.canonicalName != null) {
-      return getCanonicalNameString(reference.canonicalName);
+      return getCanonicalNameString(reference.canonicalName!);
     }
     throw "Neither node nor canonical name found";
   }
@@ -976,28 +995,28 @@
 
     String libraryString(CanonicalName lib) {
       if (lib.reference?.node != null) {
-        return getLibraryReference(lib.reference.asLibrary);
+        return getLibraryReference(lib.reference!.asLibrary);
       }
       return syntheticNames.nameCanonicalNameAsLibraryPrefix(
           lib.reference, lib);
     }
 
     String classString(CanonicalName cls) =>
-        libraryString(cls.parent) + '::' + cls.name;
+        libraryString(cls.parent!) + '::' + cls.name;
 
-    if (name.parent.isRoot) return libraryString(name);
-    if (name.parent.parent.isRoot) return classString(name);
+    if (name.parent!.isRoot) return libraryString(name);
+    if (name.parent!.parent!.isRoot) return classString(name);
 
-    CanonicalName atNode = name.parent;
+    CanonicalName atNode = name.parent!;
     while (!atNode.name.startsWith('@')) {
-      atNode = atNode.parent;
+      atNode = atNode.parent!;
     }
 
     String parent = "";
-    if (atNode.parent.parent.isRoot) {
-      parent = libraryString(atNode.parent);
+    if (atNode.parent!.parent!.isRoot) {
+      parent = libraryString(atNode.parent!);
     } else {
-      parent = classString(atNode.parent);
+      parent = classString(atNode.parent!);
     }
 
     if (name.name == '') return "$parent::$emptyNameString";
@@ -1023,7 +1042,7 @@
     writeWord(getTypeParameterReference(node));
   }
 
-  void writeExpression(Expression node, [int minimumPrecedence]) {
+  void writeExpression(Expression node, [int? minimumPrecedence]) {
     final bool highlight = shouldHighlight(node);
     if (highlight) {
       startHighlight(node);
@@ -1087,9 +1106,10 @@
     writeSpace();
     writeAnnotatedType(node.type, annotator?.annotateField(this, node));
     writeName(getMemberName(node));
-    if (node.initializer != null) {
+    Expression? initializer = node.initializer;
+    if (initializer != null) {
       writeSpaced('=');
-      writeExpression(node.initializer);
+      writeExpression(initializer);
     }
     List<String> features = <String>[];
     if (node.enclosingLibrary.isNonNullableByDefault !=
@@ -1100,10 +1120,10 @@
         features.add("isLegacy");
       }
     }
-    if ((node.enclosingClass == null &&
+    Class? enclosingClass = node.enclosingClass;
+    if ((enclosingClass == null &&
             node.enclosingLibrary.fileUri != node.fileUri) ||
-        (node.enclosingClass != null &&
-            node.enclosingClass.fileUri != node.fileUri)) {
+        (enclosingClass != null && enclosingClass.fileUri != node.fileUri)) {
       features.add(" from ${node.fileUri} ");
     }
     if (features.isNotEmpty) {
@@ -1148,10 +1168,10 @@
         features.add("isLegacy");
       }
     }
-    if ((node.enclosingClass == null &&
+    Class? enclosingClass = node.enclosingClass;
+    if ((enclosingClass == null &&
             node.enclosingLibrary.fileUri != node.fileUri) ||
-        (node.enclosingClass != null &&
-            node.enclosingClass.fileUri != node.fileUri)) {
+        (enclosingClass != null && enclosingClass.fileUri != node.fileUri)) {
       features.add(" from ${node.fileUri} ");
     }
     if (features.isNotEmpty) {
@@ -1163,17 +1183,17 @@
       case ProcedureStubKind.ConcreteForwardingStub:
       case ProcedureStubKind.NoSuchMethodForwarder:
       case ProcedureStubKind.ConcreteMixinStub:
-        writeFunction(node.function, name: getMemberName(node));
+        writeFunction(node.function!, name: getMemberName(node));
         break;
       case ProcedureStubKind.MemberSignature:
       case ProcedureStubKind.AbstractMixinStub:
-        writeFunction(node.function,
+        writeFunction(node.function!,
             name: getMemberName(node), terminateLine: false);
-        if (node.function.body is ReturnStatement) {
+        if (node.function!.body is ReturnStatement) {
           writeSymbol(';');
         }
         writeSymbol(' -> ');
-        writeMemberReferenceFromReference(node.stubTargetReference);
+        writeMemberReferenceFromReference(node.stubTargetReference!);
         endLine();
         break;
     }
@@ -1198,7 +1218,7 @@
     if (features.isNotEmpty) {
       writeWord("/*${features.join(',')}*/");
     }
-    writeFunction(node.function,
+    writeFunction(node.function!,
         name: node.name, initializers: node.initializers);
   }
 
@@ -1210,13 +1230,13 @@
     writeWord('redirecting_factory');
 
     if (node.name != null) {
-      writeName(node.name);
+      writeName(node.name!);
     }
     writeTypeParameterList(node.typeParameters);
     writeParameterList(node.positionalParameters, node.namedParameters,
         node.requiredParameterCount);
     writeSpaced('=');
-    writeMemberReferenceFromReference(node.targetReference);
+    writeMemberReferenceFromReference(node.targetReference!);
     if (node.typeArguments.isNotEmpty) {
       writeSymbol('<');
       writeList(node.typeArguments, writeType);
@@ -1246,12 +1266,12 @@
     writeTypeParameterList(node.typeParameters);
     if (node.isMixinApplication) {
       writeSpaced('=');
-      visitSupertype(node.supertype);
+      visitSupertype(node.supertype!);
       writeSpaced('with');
-      visitSupertype(node.mixedInType);
+      visitSupertype(node.mixedInType!);
     } else if (node.supertype != null) {
       writeSpaced('extends');
-      visitSupertype(node.supertype);
+      visitSupertype(node.supertype!);
     }
     if (node.implementedTypes.isNotEmpty) {
       writeSpaced('implements');
@@ -1297,7 +1317,7 @@
     writeWord(getExtensionName(node));
     writeTypeParameterList(node.typeParameters);
     writeSpaced('on');
-    writeType(node.onType);
+    writeType(node.onType!);
     String endLineString = ' {';
     if (node.enclosingLibrary.fileUri != node.fileUri) {
       endLineString += ' // from ${node.fileUri}';
@@ -1352,18 +1372,20 @@
     writeWord(node.name);
     writeTypeParameterList(node.typeParameters);
     writeSpaced('=');
-    if (node.type is FunctionType) {
-      writeFunctionType(node.type,
+    DartType? type = node.type;
+    if (type is FunctionType) {
+      writeFunctionType(type,
           typedefPositional: node.positionalParameters,
           typedefNamed: node.namedParameters);
     } else {
-      writeNode(node.type);
+      writeNode(type);
     }
     endLine(';');
   }
 
   visitInvalidExpression(InvalidExpression node) {
     writeWord('invalid-expression');
+    // ignore: unnecessary_null_comparison
     if (node.message != null) {
       writeWord('"${escapeString(node.message)}"');
     }
@@ -1415,7 +1437,7 @@
     writeNode(node.arguments);
     if (node.functionType != null) {
       writeSymbol('{');
-      writeType(node.functionType);
+      writeType(node.functionType!);
       writeSymbol('}');
     }
   }
@@ -1530,8 +1552,8 @@
   }
 
   visitLogicalExpression(LogicalExpression node) {
-    int precedence = Precedence
-        .binaryPrecedence[logicalExpressionOperatorToString(node.operatorEnum)];
+    int precedence = Precedence.binaryPrecedence[
+        logicalExpressionOperatorToString(node.operatorEnum)]!;
     writeExpression(node.left, precedence);
     writeSpaced(logicalExpressionOperatorToString(node.operatorEnum));
     writeExpression(node.right, precedence + 1);
@@ -1606,7 +1628,7 @@
       if (!first) {
         writeComma();
       }
-      writeWord('${fieldRef.asField.name.text}');
+      writeWord('${fieldRef.asField.name!.text}');
       writeSymbol(':');
       writeExpression(value);
       first = false;
@@ -1617,9 +1639,10 @@
       }
       write('assert(');
       writeExpression(assert_.condition);
-      if (assert_.message != null) {
+      Expression? message = assert_.message;
+      if (message != null) {
         writeComma();
-        writeExpression(assert_.message);
+        writeExpression(message);
       }
       write(')');
       first = false;
@@ -1692,6 +1715,7 @@
       writeWord('const');
       writeSpace();
     }
+    // ignore: unnecessary_null_comparison
     if (node.typeArgument != null) {
       writeSymbol('<');
       writeType(node.typeArgument);
@@ -1707,6 +1731,7 @@
       writeWord('const');
       writeSpace();
     }
+    // ignore: unnecessary_null_comparison
     if (node.typeArgument != null) {
       writeSymbol('<');
       writeType(node.typeArgument);
@@ -1722,6 +1747,7 @@
       writeWord('const');
       writeSpace();
     }
+    // ignore: unnecessary_null_comparison
     if (node.keyType != null) {
       writeSymbol('<');
       writeList([node.keyType, node.valueType], writeType);
@@ -1791,7 +1817,7 @@
   visitLoadLibrary(LoadLibrary node) {
     writeWord('LoadLibrary');
     writeSymbol('(');
-    writeWord(node.import.name);
+    writeWord(node.import.name!);
     writeSymbol(')');
     state = WORD;
   }
@@ -1799,7 +1825,7 @@
   visitCheckLibraryIsLoaded(CheckLibraryIsLoaded node) {
     writeWord('CheckLibraryIsLoaded');
     writeSymbol('(');
-    writeWord(node.import.name);
+    writeWord(node.import.name!);
     writeSymbol(')');
     state = WORD;
   }
@@ -1816,20 +1842,21 @@
     writeIndentation();
     writeWord(node.isImport ? 'import' : 'export');
     String uriString;
-    if (node.importedLibraryReference?.node != null) {
+    if (node.importedLibraryReference.node != null) {
       uriString = '${node.targetLibrary.importUri}';
     } else {
-      uriString = '${node.importedLibraryReference?.canonicalName?.name}';
+      uriString = '${node.importedLibraryReference.canonicalName?.name}';
     }
     writeWord('"$uriString"');
     if (node.isDeferred) {
       writeWord('deferred');
     }
-    if (node.name != null) {
+    String? name = node.name;
+    if (name != null) {
       writeWord('as');
-      writeWord(node.name);
+      writeWord(name);
     }
-    String last;
+    String? last;
     final String show = 'show';
     final String hide = 'hide';
     if (node.combinators.isNotEmpty) {
@@ -1859,9 +1886,10 @@
 
   visitVariableGet(VariableGet node) {
     writeVariableReference(node.variable);
-    if (node.promotedType != null) {
+    DartType? promotedType = node.promotedType;
+    if (promotedType != null) {
       writeSymbol('{');
-      writeNode(node.promotedType);
+      writeNode(promotedType);
       writeSymbol('}');
       state = WORD;
     }
@@ -1873,7 +1901,7 @@
     writeExpression(node.value);
   }
 
-  void writeInterfaceTarget(Name name, Reference target) {
+  void writeInterfaceTarget(Name name, Reference? target) {
     if (target != null) {
       writeSymbol('{');
       writeMemberReferenceFromReference(target);
@@ -1884,6 +1912,7 @@
   }
 
   void writeStaticType(DartType type) {
+    // ignore: unnecessary_null_comparison
     if (type != null) {
       writeSymbol('{');
       writeType(type);
@@ -2024,9 +2053,10 @@
     writeWord('assert');
     writeSymbol('(');
     writeExpression(node.condition);
-    if (node.message != null) {
+    Expression? message = node.message;
+    if (message != null) {
       writeComma();
-      writeExpression(node.message);
+      writeExpression(message);
     }
     if (!asExpression) {
       endLine(');');
@@ -2075,8 +2105,9 @@
     writeSymbol('(');
     writeList(node.variables, writeVariableDeclaration);
     writeComma(';');
-    if (node.condition != null) {
-      writeExpression(node.condition);
+    Expression? condition = node.condition;
+    if (condition != null) {
+      writeExpression(condition);
     }
     writeComma(';');
     writeList(node.updates, writeExpression);
@@ -2146,19 +2177,21 @@
     writeExpression(node.condition);
     writeSymbol(')');
     writeBody(node.then);
-    if (node.otherwise != null) {
+    Statement? otherwise = node.otherwise;
+    if (otherwise != null) {
       writeIndentation();
       writeWord('else');
-      writeBody(node.otherwise);
+      writeBody(otherwise);
     }
   }
 
   visitReturnStatement(ReturnStatement node) {
     writeIndentation();
     writeWord('return');
-    if (node.expression != null) {
+    Expression? expression = node.expression;
+    if (expression != null) {
       writeSpace();
-      writeExpression(node.expression);
+      writeExpression(expression);
     }
     endLine(';');
   }
@@ -2172,6 +2205,7 @@
 
   visitCatch(Catch node) {
     writeIndentation();
+    // ignore: unnecessary_null_comparison
     if (node.guard != null) {
       writeWord('on');
       writeType(node.guard);
@@ -2179,14 +2213,16 @@
     }
     writeWord('catch');
     writeSymbol('(');
-    if (node.exception != null) {
-      writeVariableDeclaration(node.exception);
+    VariableDeclaration? exception = node.exception;
+    if (exception != null) {
+      writeVariableDeclaration(exception);
     } else {
       writeWord('no-exception-var');
     }
-    if (node.stackTrace != null) {
+    VariableDeclaration? stackTrace = node.stackTrace;
+    if (stackTrace != null) {
       writeComma();
-      writeVariableDeclaration(node.stackTrace);
+      writeVariableDeclaration(stackTrace);
     }
     writeSymbol(')');
     writeBody(node.body);
@@ -2225,7 +2261,7 @@
     writeIndentation();
     writeWord('function');
     if (node.function != null) {
-      writeFunction(node.function, name: getVariableName(node.variable));
+      writeFunction(node.function!, name: getVariableName(node.variable));
     } else {
       writeWord(getVariableName(node.variable));
       endLine('...;');
@@ -2244,16 +2280,19 @@
     writeModifier(node.isGenericCovariantImpl, 'generic-covariant-impl');
     writeModifier(node.isFinal, 'final');
     writeModifier(node.isConst, 'const');
+    // ignore: unnecessary_null_comparison
     if (node.type != null) {
       writeAnnotatedType(node.type, annotator?.annotateVariable(this, node));
     }
+    // ignore: unnecessary_null_comparison
     if (useVarKeyword && !node.isFinal && !node.isConst && node.type == null) {
       writeWord('var');
     }
     writeWord(getVariableName(node));
-    if (node.initializer != null) {
+    Expression? initializer = node.initializer;
+    if (initializer != null) {
       writeSpaced('=');
-      writeExpression(node.initializer);
+      writeExpression(initializer);
     }
   }
 
@@ -2407,14 +2446,15 @@
   visitTypeParameterType(TypeParameterType node) {
     writeTypeParameterReference(node.parameter);
     writeNullability(node.declaredNullability);
-    if (node.promotedBound != null) {
+    DartType? promotedBound = node.promotedBound;
+    if (promotedBound != null) {
       writeSpaced('&');
-      writeType(node.promotedBound);
+      writeType(promotedBound);
 
       writeWord("/* '");
       writeNullability(node.declaredNullability, inComment: true);
       writeWord("' & '");
-      writeDartTypeNullability(node.promotedBound, inComment: true);
+      writeDartTypeNullability(promotedBound, inComment: true);
       writeWord("' = '");
       writeNullability(node.nullability, inComment: true);
       writeWord("' */");
@@ -2434,10 +2474,10 @@
     }
     writeWord(getTypeParameterName(node));
     writeSpaced('extends');
-    writeType(node.bound);
+    writeType(node.bound!);
     if (node.defaultType != null) {
       writeSpaced('=');
-      writeType(node.defaultType);
+      writeType(node.defaultType!);
     }
   }
 
@@ -2488,6 +2528,7 @@
     writeIndentation();
     writeConstantReference(node);
     writeSpaced('=');
+    // ignore: unnecessary_null_comparison
     String text = node.libraryReference != null
         ? '#${node.libraryReference.asLibrary.importUri}::${node.name}'
         : '#${node.name}';
@@ -2556,9 +2597,9 @@
     writeList(node.fieldValues.entries,
         (core.MapEntry<Reference, Constant> entry) {
       if (entry.key.node != null) {
-        writeWord('${entry.key.asField.name.text}');
+        writeWord('${entry.key.asField.name!.text}');
       } else {
-        writeWord('${entry.key.canonicalName.name}');
+        writeWord('${entry.key.canonicalName!.name}');
       }
       writeSymbol(':');
       writeConstantReference(entry.value);
@@ -2636,7 +2677,7 @@
   static const int PRIMARY = 20;
   static const int CALLEE = 21;
 
-  static const Map<String, int> binaryPrecedence = const {
+  static const Map<String?, int> binaryPrecedence = const {
     '&&': LOGICAL_AND,
     '||': LOGICAL_OR,
     '??': LOGICAL_NULL_AWARE,
@@ -2708,7 +2749,7 @@
 
   @override
   int visitLogicalExpression(LogicalExpression node) =>
-      binaryPrecedence[logicalExpressionOperatorToString(node.operatorEnum)];
+      binaryPrecedence[logicalExpressionOperatorToString(node.operatorEnum)]!;
 
   @override
   int visitConditionalExpression(ConditionalExpression node) => CONDITIONAL;
@@ -2862,5 +2903,4 @@
     case ProcedureKind.Factory:
       return 'factory';
   }
-  throw 'illegal ProcedureKind: $kind';
 }
diff --git a/pkg/kernel/lib/text/text_serializer.dart b/pkg/kernel/lib/text/text_serializer.dart
index 3ababf6..38582df 100644
--- a/pkg/kernel/lib/text/text_serializer.dart
+++ b/pkg/kernel/lib/text/text_serializer.dart
@@ -1089,10 +1089,15 @@
 }
 
 TextSerializer<AssertStatement> assertStatementSerializer =
-    Wrapped<Tuple2<Expression, Expression>, AssertStatement>(
-        (a) => Tuple2(a.condition, a.message),
-        (t) => AssertStatement(t.first, message: t.second),
-        Tuple2Serializer(expressionSerializer, Optional(expressionSerializer)));
+    Wrapped<Tuple4<Expression, Expression, int, int>, AssertStatement>(
+        (a) => Tuple4(a.condition, a.message, a.conditionStartOffset,
+            a.conditionEndOffset),
+        (t) => AssertStatement(t.first,
+            message: t.second,
+            conditionStartOffset: t.third,
+            conditionEndOffset: t.fourth),
+        Tuple4Serializer(expressionSerializer, Optional(expressionSerializer),
+            const DartInt(), const DartInt()));
 
 TextSerializer<Block> blockSerializer =
     Wrapped<Tuple2<List<Statement>, Expression>, Block>(
@@ -2102,11 +2107,9 @@
     Wrapped<Tuple4<Name, ExtensionMemberKind, int, CanonicalName>,
             ExtensionMemberDescriptor>(
         (w) => Tuple4(w.name, w.kind, w.flags, w.member.canonicalName),
-        (u) => ExtensionMemberDescriptor()
-          ..name = u.first
-          ..kind = u.second
-          ..flags = u.third
-          ..member = u.fourth.getReference(),
+        (u) => ExtensionMemberDescriptor(
+            name: u.first, kind: u.second, member: u.fourth.getReference())
+          ..flags = u.third,
         Tuple4Serializer(
             nameSerializer,
             extensionMemberKindSerializer,
diff --git a/pkg/kernel/lib/transformations/async.dart b/pkg/kernel/lib/transformations/async.dart
index e8ac7ba..736c9cf 100644
--- a/pkg/kernel/lib/transformations/async.dart
+++ b/pkg/kernel/lib/transformations/async.dart
@@ -177,7 +177,7 @@
   // Transform an expression given an action to transform the children.  For
   // this purposes of the await transformer the children should generally be
   // translated from right to left, in the reverse of evaluation order.
-  Expression transform(Expression expr, void action()) {
+  Expression transformTreeNode(Expression expr, void action()) {
     var shouldName = seenAwait;
 
     // 1. If there is an await in a sibling to the right, emit an assignment to
@@ -208,7 +208,7 @@
 
   // Unary expressions.
   Expression unary(Expression expr) {
-    return transform(expr, () {
+    return transformTreeNode(expr, () {
       expr.transformChildren(this);
     });
   }
@@ -223,7 +223,7 @@
   TreeNode visitThrow(Throw expr) => unary(expr);
 
   TreeNode visitPropertySet(PropertySet expr) {
-    return transform(expr, () {
+    return transformTreeNode(expr, () {
       expr.value = expr.value.accept<TreeNode>(this)..parent = expr;
       expr.receiver = expr.receiver.accept<TreeNode>(this)..parent = expr;
     });
@@ -243,32 +243,32 @@
   }
 
   TreeNode visitMethodInvocation(MethodInvocation expr) {
-    return transform(expr, () {
+    return transformTreeNode(expr, () {
       visitArguments(expr.arguments);
       expr.receiver = expr.receiver.accept<TreeNode>(this)..parent = expr;
     });
   }
 
   TreeNode visitSuperMethodInvocation(SuperMethodInvocation expr) {
-    return transform(expr, () {
+    return transformTreeNode(expr, () {
       visitArguments(expr.arguments);
     });
   }
 
   TreeNode visitStaticInvocation(StaticInvocation expr) {
-    return transform(expr, () {
+    return transformTreeNode(expr, () {
       visitArguments(expr.arguments);
     });
   }
 
   TreeNode visitConstructorInvocation(ConstructorInvocation expr) {
-    return transform(expr, () {
+    return transformTreeNode(expr, () {
       visitArguments(expr.arguments);
     });
   }
 
   TreeNode visitStringConcatenation(StringConcatenation expr) {
-    return transform(expr, () {
+    return transformTreeNode(expr, () {
       var expressions = expr.expressions;
       for (var i = expressions.length - 1; i >= 0; --i) {
         expressions[i] = expressions[i].accept<TreeNode>(this)..parent = expr;
@@ -277,7 +277,7 @@
   }
 
   TreeNode visitListLiteral(ListLiteral expr) {
-    return transform(expr, () {
+    return transformTreeNode(expr, () {
       var expressions = expr.expressions;
       for (var i = expressions.length - 1; i >= 0; --i) {
         expressions[i] = expr.expressions[i].accept<TreeNode>(this)
@@ -287,7 +287,7 @@
   }
 
   TreeNode visitMapLiteral(MapLiteral expr) {
-    return transform(expr, () {
+    return transformTreeNode(expr, () {
       for (var entry in expr.entries.reversed) {
         entry.value = entry.value.accept<TreeNode>(this)..parent = entry;
         entry.key = entry.key.accept<TreeNode>(this)..parent = entry;
@@ -310,7 +310,7 @@
     if (rightStatements.isEmpty) {
       // Easy case: right did not emit any statements.
       seenAwait = shouldName;
-      return transform(expr, () {
+      return transformTreeNode(expr, () {
         expr.left = expr.left.accept<TreeNode>(this)..parent = expr;
         seenAwait = seenAwait || rightAwait;
       });
@@ -391,7 +391,7 @@
     if (thenStatements.isEmpty && otherwiseStatements.isEmpty) {
       // Easy case: neither then nor otherwise emitted any statements.
       seenAwait = shouldName;
-      return transform(expr, () {
+      return transformTreeNode(expr, () {
         expr.condition = expr.condition.accept<TreeNode>(this)..parent = expr;
         seenAwait = seenAwait || thenAwait || otherwiseAwait;
       });
@@ -518,7 +518,7 @@
     } else {
       // The body in `let x = initializer in body` did not contain an await.  We
       // can leave a let expression.
-      return transform(expr, () {
+      return transformTreeNode(expr, () {
         // The body has already been translated.
         expr.body = body..parent = expr;
         variable.initializer = variable.initializer.accept<TreeNode>(this)
@@ -534,7 +534,7 @@
   }
 
   TreeNode visitBlockExpression(BlockExpression expr) {
-    return transform(expr, () {
+    return transformTreeNode(expr, () {
       expr.value = expr.value.accept<TreeNode>(this)..parent = expr;
       List<Statement> body = <Statement>[];
       for (Statement stmt in expr.body.statements.reversed) {
diff --git a/pkg/kernel/lib/transformations/continuation.dart b/pkg/kernel/lib/transformations/continuation.dart
index 0df3e84..24ed26c 100644
--- a/pkg/kernel/lib/transformations/continuation.dart
+++ b/pkg/kernel/lib/transformations/continuation.dart
@@ -296,7 +296,7 @@
     }
 
     ++currentCatchDepth;
-    transformList(node.catches, this, node);
+    transformList(node.catches, node);
     --currentCatchDepth;
     return node;
   }
diff --git a/pkg/kernel/lib/transformations/flags.dart b/pkg/kernel/lib/transformations/flags.dart
index 398afeb..f6d2775 100644
--- a/pkg/kernel/lib/transformations/flags.dart
+++ b/pkg/kernel/lib/transformations/flags.dart
@@ -4,7 +4,6 @@
 
 library kernel.transformations.flags;
 
-// ignore: import_of_legacy_library_into_null_safe
 import '../ast.dart';
 
 /// Flags summarizing the kinds of AST nodes contained in a given member or
diff --git a/pkg/kernel/lib/type_algebra.dart b/pkg/kernel/lib/type_algebra.dart
index 18c4924..0fb8863 100644
--- a/pkg/kernel/lib/type_algebra.dart
+++ b/pkg/kernel/lib/type_algebra.dart
@@ -4,7 +4,6 @@
 
 library kernel.type_algebra;
 
-// ignore: import_of_legacy_library_into_null_safe
 import 'ast.dart';
 import 'core_types.dart';
 import 'src/replacement_visitor.dart';
@@ -41,7 +40,7 @@
     result[parameter] = const DynamicType();
   }
   for (TypeParameter parameter in host.typeParameters) {
-    result[parameter] = substitute(parameter.bound, result);
+    result[parameter] = substitute(parameter.bound!, result);
   }
   return result;
 }
@@ -121,9 +120,9 @@
     TypeParameter typeParameter = typeParameters[i];
     TypeParameter freshTypeParameter = freshParameters[i];
 
-    freshTypeParameter.bound = substitute(typeParameter.bound, map);
+    freshTypeParameter.bound = substitute(typeParameter.bound!, map);
     freshTypeParameter.defaultType = typeParameter.defaultType != null
-        ? substitute(typeParameter.defaultType, map)
+        ? substitute(typeParameter.defaultType!, map)
         : null;
     freshTypeParameter.variance =
         typeParameter.isLegacyCovariant ? null : typeParameter.variance;
@@ -148,7 +147,7 @@
         requiredParameterCount: type.requiredParameterCount,
         typedefType: type.typedefType == null
             ? null
-            : substitute(type.typedefType) as TypedefType);
+            : substitute(type.typedefType!) as TypedefType);
   }
 
   DartType substitute(DartType type) => substitution.substituteType(type);
@@ -250,7 +249,7 @@
       upper[parameter] = const DynamicType();
     }
     for (TypeParameter parameter in class_.typeParameters) {
-      upper[parameter] = substitute(parameter.bound, upper);
+      upper[parameter] = substitute(parameter.bound!, upper);
     }
     return fromUpperAndLowerBounds(upper, {});
   }
@@ -384,9 +383,9 @@
     TypeParameter fresh = new TypeParameter(node.name);
     TypeParameterType typeParameterType = substitution[node] =
         new TypeParameterType.forAlphaRenaming(node, fresh);
-    fresh.bound = visit(node.bound);
+    fresh.bound = visit(node.bound!);
     if (node.defaultType != null) {
-      fresh.defaultType = visit(node.defaultType);
+      fresh.defaultType = visit(node.defaultType!);
     }
     // If the bound was changed from substituting the bound we need to update
     // implicit nullability to be based on the new bound. If the bound wasn't
@@ -561,7 +560,7 @@
     DartType returnType = inner.visit(node.returnType);
     TypedefType? typedefType = node.typedefType == null
         ? null
-        : inner.visit(node.typedefType) as TypedefType;
+        : inner.visit(node.typedefType!) as TypedefType;
     if (this.useCounter == before) return node;
     return new FunctionType(positionalParameters, returnType, node.nullability,
         namedParameters: namedParameters,
@@ -699,9 +698,9 @@
 
   bool handleTypeParameter(TypeParameter node) {
     assert(!variables.contains(node));
-    if (node.bound.accept(this)) return true;
+    if (node.bound!.accept(this)) return true;
     if (node.defaultType == null) return false;
-    return node.defaultType.accept(this);
+    return node.defaultType!.accept(this);
   }
 }
 
@@ -755,9 +754,9 @@
 
   bool handleTypeParameter(TypeParameter node) {
     assert(variables.contains(node));
-    if (node.bound.accept(this)) return true;
+    if (node.bound!.accept(this)) return true;
     if (node.defaultType == null) return false;
-    return node.defaultType.accept(this);
+    return node.defaultType!.accept(this);
   }
 }
 
@@ -811,9 +810,9 @@
 
   bool handleTypeParameter(TypeParameter node) {
     assert(variables.contains(node));
-    if (node.bound.accept(this)) return true;
+    if (node.bound!.accept(this)) return true;
     if (node.defaultType == null) return false;
-    return node.defaultType.accept(this);
+    return node.defaultType!.accept(this);
   }
 }
 
@@ -1057,7 +1056,7 @@
     //  - The greatest closure of `S` with respect to `L` is `Function`
     if (node.typeParameters.isNotEmpty) {
       for (TypeParameter typeParameter in node.typeParameters) {
-        if (containsTypeVariable(typeParameter.bound, eliminationTargets,
+        if (containsTypeVariable(typeParameter.bound!, eliminationTargets,
             unhandledTypeHandler: unhandledTypeHandler)) {
           return getFunctionReplacement(variance);
         }
diff --git a/pkg/kernel/lib/type_environment.dart b/pkg/kernel/lib/type_environment.dart
index da45c08..c8789ae 100644
--- a/pkg/kernel/lib/type_environment.dart
+++ b/pkg/kernel/lib/type_environment.dart
@@ -4,7 +4,6 @@
 
 library kernel.type_environment;
 
-// ignore: import_of_legacy_library_into_null_safe
 import 'ast.dart';
 import 'class_hierarchy.dart';
 import 'core_types.dart';
@@ -115,8 +114,7 @@
   DartType _resolveTypeParameterType(DartType type) {
     while (type is TypeParameterType) {
       TypeParameterType typeParameterType = type;
-      type =
-          typeParameterType.promotedBound ?? typeParameterType.parameter.bound;
+      type = typeParameterType.bound;
     }
     return type;
   }
@@ -148,13 +146,13 @@
   bool isSpecialCasedBinaryOperator(Procedure member,
       {bool isNonNullableByDefault: false}) {
     if (isNonNullableByDefault) {
-      Class class_ = member.enclosingClass;
+      Class? class_ = member.enclosingClass;
       // TODO(johnniwinther): Do we need to recognize backend implementation
       //  methods?
       if (class_ == coreTypes.intClass ||
           class_ == coreTypes.numClass ||
           class_ == coreTypes.doubleClass) {
-        String name = member.name.text;
+        String name = member.name!.text;
         return name == '+' ||
             name == '-' ||
             name == '*' ||
@@ -162,9 +160,9 @@
             name == '%';
       }
     } else {
-      Class class_ = member.enclosingClass;
+      Class? class_ = member.enclosingClass;
       if (class_ == coreTypes.intClass || class_ == coreTypes.numClass) {
-        String name = member.name.text;
+        String name = member.name!.text;
         return name == '+' ||
             name == '-' ||
             name == '*' ||
@@ -180,9 +178,9 @@
   bool isSpecialCasedTernaryOperator(Procedure member,
       {bool isNonNullableByDefault: false}) {
     if (isNonNullableByDefault) {
-      Class class_ = member.enclosingClass;
+      Class? class_ = member.enclosingClass;
       if (class_ == coreTypes.intClass || class_ == coreTypes.numClass) {
-        String name = member.name.text;
+        String name = member.name!.text;
         return name == 'clamp';
       }
     }
@@ -751,7 +749,7 @@
         "No member currently associated with StaticTypeContext.");
     return _currentMember?.enclosingClass?.getThisType(
         typeEnvironment.coreTypes,
-        _currentMember?.enclosingLibrary.nonNullable);
+        _currentMember!.enclosingLibrary.nonNullable);
   }
 
   @override
diff --git a/pkg/kernel/lib/visitor.dart b/pkg/kernel/lib/visitor.dart
index c5b02c5..13e7f14 100644
--- a/pkg/kernel/lib/visitor.dart
+++ b/pkg/kernel/lib/visitor.dart
@@ -6,7 +6,6 @@
 
 import 'dart:collection';
 
-// ignore: import_of_legacy_library_into_null_safe
 import 'ast.dart';
 
 abstract class ExpressionVisitor<R> {
@@ -126,6 +125,20 @@
   }
 }
 
+abstract class MemberVisitor1<R, A> {
+  const MemberVisitor1();
+
+  R defaultMember(Member node, A arg);
+
+  R visitConstructor(Constructor node, A arg) => defaultMember(node, arg);
+  R visitProcedure(Procedure node, A arg) => defaultMember(node, arg);
+  R visitField(Field node, A arg) => defaultMember(node, arg);
+  R visitRedirectingFactoryConstructor(
+      RedirectingFactoryConstructor node, A arg) {
+    return defaultMember(node, arg);
+  }
+}
+
 abstract class InitializerVisitor<R> {
   const InitializerVisitor();
 
@@ -141,6 +154,25 @@
   R visitAssertInitializer(AssertInitializer node) => defaultInitializer(node);
 }
 
+abstract class InitializerVisitor1<R, A> {
+  const InitializerVisitor1();
+
+  R defaultInitializer(Initializer node, A arg);
+
+  R visitInvalidInitializer(InvalidInitializer node, A arg) =>
+      defaultInitializer(node, arg);
+  R visitFieldInitializer(FieldInitializer node, A arg) =>
+      defaultInitializer(node, arg);
+  R visitSuperInitializer(SuperInitializer node, A arg) =>
+      defaultInitializer(node, arg);
+  R visitRedirectingInitializer(RedirectingInitializer node, A arg) =>
+      defaultInitializer(node, arg);
+  R visitLocalInitializer(LocalInitializer node, A arg) =>
+      defaultInitializer(node, arg);
+  R visitAssertInitializer(AssertInitializer node, A arg) =>
+      defaultInitializer(node, arg);
+}
+
 abstract class TreeVisitor<R>
     implements
         ExpressionVisitor<R>,
@@ -287,6 +319,195 @@
   R visitComponent(Component node) => defaultTreeNode(node);
 }
 
+abstract class TreeVisitor1<R, A>
+    implements
+        ExpressionVisitor1<R, A>,
+        StatementVisitor1<R, A>,
+        MemberVisitor1<R, A>,
+        InitializerVisitor1<R, A> {
+  const TreeVisitor1();
+
+  R defaultTreeNode(TreeNode node, A arg);
+
+  // Expressions
+  R defaultExpression(Expression node, A arg) => defaultTreeNode(node, arg);
+  R defaultBasicLiteral(BasicLiteral node, A arg) =>
+      defaultExpression(node, arg);
+  R visitInvalidExpression(InvalidExpression node, A arg) =>
+      defaultExpression(node, arg);
+  R visitVariableGet(VariableGet node, A arg) => defaultExpression(node, arg);
+  R visitVariableSet(VariableSet node, A arg) => defaultExpression(node, arg);
+  R visitDynamicGet(DynamicGet node, A arg) => defaultExpression(node, arg);
+  R visitDynamicSet(DynamicSet node, A arg) => defaultExpression(node, arg);
+  R visitFunctionTearOff(FunctionTearOff node, A arg) =>
+      defaultExpression(node, arg);
+  R visitInstanceGet(InstanceGet node, A arg) => defaultExpression(node, arg);
+  R visitInstanceSet(InstanceSet node, A arg) => defaultExpression(node, arg);
+  R visitInstanceTearOff(InstanceTearOff node, A arg) =>
+      defaultExpression(node, arg);
+  R visitPropertyGet(PropertyGet node, A arg) => defaultExpression(node, arg);
+  R visitPropertySet(PropertySet node, A arg) => defaultExpression(node, arg);
+  R visitSuperPropertyGet(SuperPropertyGet node, A arg) =>
+      defaultExpression(node, arg);
+  R visitSuperPropertySet(SuperPropertySet node, A arg) =>
+      defaultExpression(node, arg);
+  R visitStaticGet(StaticGet node, A arg) => defaultExpression(node, arg);
+  R visitStaticSet(StaticSet node, A arg) => defaultExpression(node, arg);
+  R visitStaticTearOff(StaticTearOff node, A arg) =>
+      defaultExpression(node, arg);
+  R visitLocalFunctionInvocation(LocalFunctionInvocation node, A arg) =>
+      defaultExpression(node, arg);
+  R visitDynamicInvocation(DynamicInvocation node, A arg) =>
+      defaultExpression(node, arg);
+  R visitFunctionInvocation(FunctionInvocation node, A arg) =>
+      defaultExpression(node, arg);
+  R visitInstanceInvocation(InstanceInvocation node, A arg) =>
+      defaultExpression(node, arg);
+  R visitEqualsNull(EqualsNull node, A arg) => defaultExpression(node, arg);
+  R visitEqualsCall(EqualsCall node, A arg) => defaultExpression(node, arg);
+  R visitMethodInvocation(MethodInvocation node, A arg) =>
+      defaultExpression(node, arg);
+  R visitSuperMethodInvocation(SuperMethodInvocation node, A arg) =>
+      defaultExpression(node, arg);
+  R visitStaticInvocation(StaticInvocation node, A arg) =>
+      defaultExpression(node, arg);
+  R visitConstructorInvocation(ConstructorInvocation node, A arg) =>
+      defaultExpression(node, arg);
+  R visitNot(Not node, A arg) => defaultExpression(node, arg);
+  R visitNullCheck(NullCheck node, A arg) => defaultExpression(node, arg);
+  R visitLogicalExpression(LogicalExpression node, A arg) =>
+      defaultExpression(node, arg);
+  R visitConditionalExpression(ConditionalExpression node, A arg) =>
+      defaultExpression(node, arg);
+  R visitStringConcatenation(StringConcatenation node, A arg) =>
+      defaultExpression(node, arg);
+  R visitListConcatenation(ListConcatenation node, A arg) =>
+      defaultExpression(node, arg);
+  R visitSetConcatenation(SetConcatenation node, A arg) =>
+      defaultExpression(node, arg);
+  R visitMapConcatenation(MapConcatenation node, A arg) =>
+      defaultExpression(node, arg);
+  R visitInstanceCreation(InstanceCreation node, A arg) =>
+      defaultExpression(node, arg);
+  R visitFileUriExpression(FileUriExpression node, A arg) =>
+      defaultExpression(node, arg);
+  R visitIsExpression(IsExpression node, A arg) => defaultExpression(node, arg);
+  R visitAsExpression(AsExpression node, A arg) => defaultExpression(node, arg);
+  R visitSymbolLiteral(SymbolLiteral node, A arg) =>
+      defaultExpression(node, arg);
+  R visitTypeLiteral(TypeLiteral node, A arg) => defaultExpression(node, arg);
+  R visitThisExpression(ThisExpression node, A arg) =>
+      defaultExpression(node, arg);
+  R visitRethrow(Rethrow node, A arg) => defaultExpression(node, arg);
+  R visitThrow(Throw node, A arg) => defaultExpression(node, arg);
+  R visitListLiteral(ListLiteral node, A arg) => defaultExpression(node, arg);
+  R visitSetLiteral(SetLiteral node, A arg) => defaultExpression(node, arg);
+  R visitMapLiteral(MapLiteral node, A arg) => defaultExpression(node, arg);
+  R visitAwaitExpression(AwaitExpression node, A arg) =>
+      defaultExpression(node, arg);
+  R visitFunctionExpression(FunctionExpression node, A arg) =>
+      defaultExpression(node, arg);
+  R visitConstantExpression(ConstantExpression node, A arg) =>
+      defaultExpression(node, arg);
+  R visitStringLiteral(StringLiteral node, A arg) =>
+      defaultBasicLiteral(node, arg);
+  R visitIntLiteral(IntLiteral node, A arg) => defaultBasicLiteral(node, arg);
+  R visitDoubleLiteral(DoubleLiteral node, A arg) =>
+      defaultBasicLiteral(node, arg);
+  R visitBoolLiteral(BoolLiteral node, A arg) => defaultBasicLiteral(node, arg);
+  R visitNullLiteral(NullLiteral node, A arg) => defaultBasicLiteral(node, arg);
+  R visitLet(Let node, A arg) => defaultExpression(node, arg);
+  R visitBlockExpression(BlockExpression node, A arg) =>
+      defaultExpression(node, arg);
+  R visitInstantiation(Instantiation node, A arg) =>
+      defaultExpression(node, arg);
+  R visitLoadLibrary(LoadLibrary node, A arg) => defaultExpression(node, arg);
+  R visitCheckLibraryIsLoaded(CheckLibraryIsLoaded node, A arg) =>
+      defaultExpression(node, arg);
+
+  // Statements
+  R defaultStatement(Statement node, A arg) => defaultTreeNode(node, arg);
+  R visitExpressionStatement(ExpressionStatement node, A arg) =>
+      defaultStatement(node, arg);
+  R visitBlock(Block node, A arg) => defaultStatement(node, arg);
+  R visitAssertBlock(AssertBlock node, A arg) => defaultStatement(node, arg);
+  R visitEmptyStatement(EmptyStatement node, A arg) =>
+      defaultStatement(node, arg);
+  R visitAssertStatement(AssertStatement node, A arg) =>
+      defaultStatement(node, arg);
+  R visitLabeledStatement(LabeledStatement node, A arg) =>
+      defaultStatement(node, arg);
+  R visitBreakStatement(BreakStatement node, A arg) =>
+      defaultStatement(node, arg);
+  R visitWhileStatement(WhileStatement node, A arg) =>
+      defaultStatement(node, arg);
+  R visitDoStatement(DoStatement node, A arg) => defaultStatement(node, arg);
+  R visitForStatement(ForStatement node, A arg) => defaultStatement(node, arg);
+  R visitForInStatement(ForInStatement node, A arg) =>
+      defaultStatement(node, arg);
+  R visitSwitchStatement(SwitchStatement node, A arg) =>
+      defaultStatement(node, arg);
+  R visitContinueSwitchStatement(ContinueSwitchStatement node, A arg) =>
+      defaultStatement(node, arg);
+  R visitIfStatement(IfStatement node, A arg) => defaultStatement(node, arg);
+  R visitReturnStatement(ReturnStatement node, A arg) =>
+      defaultStatement(node, arg);
+  R visitTryCatch(TryCatch node, A arg) => defaultStatement(node, arg);
+  R visitTryFinally(TryFinally node, A arg) => defaultStatement(node, arg);
+  R visitYieldStatement(YieldStatement node, A arg) =>
+      defaultStatement(node, arg);
+  R visitVariableDeclaration(VariableDeclaration node, A arg) =>
+      defaultStatement(node, arg);
+  R visitFunctionDeclaration(FunctionDeclaration node, A arg) =>
+      defaultStatement(node, arg);
+
+  // Members
+  R defaultMember(Member node, A arg) => defaultTreeNode(node, arg);
+  R visitConstructor(Constructor node, A arg) => defaultMember(node, arg);
+  R visitProcedure(Procedure node, A arg) => defaultMember(node, arg);
+  R visitField(Field node, A arg) => defaultMember(node, arg);
+  R visitRedirectingFactoryConstructor(
+      RedirectingFactoryConstructor node, A arg) {
+    return defaultMember(node, arg);
+  }
+
+  // Classes
+  R visitClass(Class node, A arg) => defaultTreeNode(node, arg);
+  R visitExtension(Extension node, A arg) => defaultTreeNode(node, arg);
+
+  // Initializers
+  R defaultInitializer(Initializer node, A arg) => defaultTreeNode(node, arg);
+  R visitInvalidInitializer(InvalidInitializer node, A arg) =>
+      defaultInitializer(node, arg);
+  R visitFieldInitializer(FieldInitializer node, A arg) =>
+      defaultInitializer(node, arg);
+  R visitSuperInitializer(SuperInitializer node, A arg) =>
+      defaultInitializer(node, arg);
+  R visitRedirectingInitializer(RedirectingInitializer node, A arg) =>
+      defaultInitializer(node, arg);
+  R visitLocalInitializer(LocalInitializer node, A arg) =>
+      defaultInitializer(node, arg);
+  R visitAssertInitializer(AssertInitializer node, A arg) =>
+      defaultInitializer(node, arg);
+
+  // Other tree nodes
+  R visitLibrary(Library node, A arg) => defaultTreeNode(node, arg);
+  R visitLibraryDependency(LibraryDependency node, A arg) =>
+      defaultTreeNode(node, arg);
+  R visitCombinator(Combinator node, A arg) => defaultTreeNode(node, arg);
+  R visitLibraryPart(LibraryPart node, A arg) => defaultTreeNode(node, arg);
+  R visitTypedef(Typedef node, A arg) => defaultTreeNode(node, arg);
+  R visitTypeParameter(TypeParameter node, A arg) => defaultTreeNode(node, arg);
+  R visitFunctionNode(FunctionNode node, A arg) => defaultTreeNode(node, arg);
+  R visitArguments(Arguments node, A arg) => defaultTreeNode(node, arg);
+  R visitNamedExpression(NamedExpression node, A arg) =>
+      defaultTreeNode(node, arg);
+  R visitSwitchCase(SwitchCase node, A arg) => defaultTreeNode(node, arg);
+  R visitCatch(Catch node, A arg) => defaultTreeNode(node, arg);
+  R visitMapEntry(MapEntry node, A arg) => defaultTreeNode(node, arg);
+  R visitComponent(Component node, A arg) => defaultTreeNode(node, arg);
+}
+
 abstract class DartTypeVisitor<R> {
   const DartTypeVisitor();
 
@@ -775,6 +996,54 @@
 class Transformer extends TreeVisitor<TreeNode> {
   const Transformer();
 
+  T transform<T extends TreeNode>(T node) {
+    T result = node.accept<TreeNode>(this) as T;
+    assert(
+        // ignore: unnecessary_null_comparison
+        result != null,
+        'Attempting to remove ${node} (${node.runtimeType}) '
+        'in transformer.');
+    return result;
+  }
+
+  void transformDartTypeList(List<DartType> nodes) {
+    for (int i = 0; i < nodes.length; ++i) {
+      DartType result = visitDartType(nodes[i]);
+      assert(
+          // ignore: unnecessary_null_comparison
+          result != null,
+          'Attempting to remove ${nodes[i]} (${nodes[i].runtimeType}) '
+          'in transformer.');
+      nodes[i] = result;
+    }
+  }
+
+  void transformSupertypeList(List<Supertype> nodes) {
+    for (int i = 0; i < nodes.length; ++i) {
+      Supertype result = visitSupertype(nodes[i]);
+      assert(
+          // ignore: unnecessary_null_comparison
+          result != null,
+          'Attempting to remove ${nodes[i]} (${nodes[i].runtimeType}) '
+          'in transformer.');
+      nodes[i] = result;
+    }
+  }
+
+  void transformList<T extends TreeNode>(List<T> nodes, TreeNode parent) {
+    for (int i = 0; i < nodes.length; ++i) {
+      T result = transform(nodes[i]);
+      assert(
+          // ignore: unnecessary_null_comparison
+          result != null,
+          'Attempting to remove ${nodes[i]} (${nodes[i].runtimeType}) '
+          'in transformer.');
+      // ignore: invalid_null_aware_operator
+      result.parent = parent;
+      nodes[i] = result;
+    }
+  }
+
   /// Replaces a use of a type.
   ///
   /// By default, recursion stops at this point.
@@ -790,6 +1059,362 @@
   }
 }
 
+/// Transformer that recursively rewrites each node in tree and supports removal
+/// of nodes.
+///
+/// Visit methods should return a new node, the visited node (possibly
+/// mutated), any node from the visited node's subtree, or the provided
+/// removal sentinel, if non-null.
+///
+/// To support removal of nodes during traversal, while enforcing nullability
+/// invariants, this visitor takes an argument, the removal sentinel. If a
+/// node is visited in a context where it can be removed, for instance in a
+/// list or as an optional child of its parent, a non-null sentinel value is
+/// provided, and this value can be returned to signal to the caller that the
+/// visited node should be removed. If the sentinel value is `null`, the node
+/// cannot be removed from its context, in which case the node itself or a new
+/// non-null node must be returned, possibly a sentinel value specific to the
+/// particular visitor.
+///
+/// For instance
+///
+///     class AssertRemover extends RemovingTransformer {
+///        @override
+///        TreeNode visitAssertStatement(
+///            AssertStatement node,
+///            TreeNode? removalSentinel) {
+///          return removalSentinel ?? new EmptyStatement();
+///        }
+///
+///        @override
+///        TreeNode visitIfStatement(
+///            IfStatement node,
+///            TreeNode? removalSentinel) {
+///          node.transformOrRemoveChildren(this);
+///          if (node.then is EmptyStatement) {
+///            if (node.otherwise != null) {
+///              return new IfStatement(
+///                  new Not(node.condition), node.otherwise);
+///            } else {
+///              return removalSentinel ?? new EmptyStatement();
+///            }
+///          }
+///          return node;
+///        }
+///     }
+///
+/// Each subclass is responsible for ensuring that the AST remains a tree.
+///
+/// For example, the following transformer replaces every occurrence of
+/// `!(x && y)` with `(!x || !y)`:
+///
+///     class NegationSinker extends RemovingTransformer {
+///       @override
+///       Node visitNot(Not node) {
+///         var operand = node.operand.accept(this); // Remember to visit.
+///         if (operand is LogicalExpression && operand.operator == '&&') {
+///           return new LogicalExpression(
+///             new Not(operand.left),
+///             '||',
+///             new Not(operand.right));
+///         }
+///         return node;
+///       }
+///     }
+///
+class RemovingTransformer extends TreeVisitor1<TreeNode, TreeNode?> {
+  const RemovingTransformer();
+
+  /// Visits [node], returning the transformation result.
+  ///
+  /// The transformation cannot result in `null`.
+  T transform<T extends TreeNode>(T node) {
+    return node.accept1<TreeNode, TreeNode?>(this, cannotRemoveSentinel) as T;
+  }
+
+  /// Visits [node], returning the transformation result. Removal of [node] is
+  /// supported with `null` as the result.
+  ///
+  /// This is convenience method for calling [transformOrRemove] with removal
+  /// sentinel for [Expression] nodes.
+  Expression? transformOrRemoveExpression(Expression node) {
+    return transformOrRemove(node, dummyExpression);
+  }
+
+  /// Visits [node], returning the transformation result. Removal of [node] is
+  /// supported with `null` as the result.
+  ///
+  /// This is convenience method for calling [transformOrRemove] with removal
+  /// sentinel for [Statement] nodes.
+  Statement? transformOrRemoveStatement(Statement node) {
+    return transformOrRemove(node, dummyStatement);
+  }
+
+  /// Visits [node], returning the transformation result. Removal of [node] is
+  /// supported with `null` as the result.
+  ///
+  /// This is convenience method for calling [transformOrRemove] with removal
+  /// sentinel for [VariableDeclaration] nodes.
+  VariableDeclaration? transformOrRemoveVariableDeclaration(
+      VariableDeclaration node) {
+    return transformOrRemove(node, dummyVariableDeclaration);
+  }
+
+  /// Visits [node] using [removalSentinel] as the removal sentinel.
+  ///
+  /// If [removalSentinel] is the result of visiting [node], `null` is returned.
+  /// Otherwise the result is returned.
+  T? transformOrRemove<T extends TreeNode>(T node, T? removalSentinel) {
+    T result = node.accept1<TreeNode, TreeNode?>(this, removalSentinel) as T;
+    if (identical(result, removalSentinel)) {
+      return null;
+    } else {
+      return result;
+    }
+  }
+
+  /// Transforms or removes [DartType] nodes in [nodes].
+  void transformDartTypeList(List<DartType> nodes) {
+    int storeIndex = 0;
+    for (int i = 0; i < nodes.length; ++i) {
+      DartType result = visitDartType(nodes[i], dummyDartType);
+      if (!identical(result, dummyDartType)) {
+        nodes[storeIndex] = result;
+        ++storeIndex;
+      }
+    }
+    if (storeIndex < nodes.length) {
+      nodes.length = storeIndex;
+    }
+  }
+
+  /// Transforms or removes [Supertype] nodes in [nodes].
+  void transformSupertypeList(List<Supertype> nodes) {
+    int storeIndex = 0;
+    for (int i = 0; i < nodes.length; ++i) {
+      Supertype result = visitSupertype(nodes[i], dummySupertype);
+      if (!identical(result, dummySupertype)) {
+        nodes[storeIndex] = result;
+        ++storeIndex;
+      }
+    }
+    if (storeIndex < nodes.length) {
+      nodes.length = storeIndex;
+    }
+  }
+
+  /// Transforms or removes [Library] nodes in [nodes] as children of [parent].
+  ///
+  /// This is convenience method for calling [transformList] with removal
+  /// sentinel for [Library] nodes.
+  void transformLibraryList(List<Library> nodes, TreeNode parent) {
+    transformList(nodes, parent, dummyLibrary);
+  }
+
+  /// Transforms or removes [LibraryDependency] nodes in [nodes] as children of
+  /// [parent].
+  ///
+  /// This is convenience method for calling [transformList] with removal
+  /// sentinel for [LibraryDependency] nodes.
+  void transformLibraryDependencyList(
+      List<LibraryDependency> nodes, TreeNode parent) {
+    transformList(nodes, parent, dummyLibraryDependency);
+  }
+
+  /// Transforms or removes [Combinator] nodes in [nodes] as children of
+  /// [parent].
+  ///
+  /// This is convenience method for calling [transformList] with removal
+  /// sentinel for [Combinator] nodes.
+  void transformCombinatorList(List<Combinator> nodes, TreeNode parent) {
+    transformList(nodes, parent, dummyCombinator);
+  }
+
+  /// Transforms or removes [LibraryPart] nodes in [nodes] as children of
+  /// [parent].
+  ///
+  /// This is convenience method for calling [transformList] with removal
+  /// sentinel for [LibraryPart] nodes.
+  void transformLibraryPartList(List<LibraryPart> nodes, TreeNode parent) {
+    transformList(nodes, parent, dummyLibraryPart);
+  }
+
+  /// Transforms or removes [Class] nodes in [nodes] as children of [parent].
+  ///
+  /// This is convenience method for calling [transformList] with removal
+  /// sentinel for [Class] nodes.
+  void transformClassList(List<Class> nodes, TreeNode parent) {
+    transformList(nodes, parent, dummyClass);
+  }
+
+  /// Transforms or removes [Extension] nodes in [nodes] as children of
+  /// [parent].
+  ///
+  /// This is convenience method for calling [transformList] with removal
+  /// sentinel for [Extension] nodes.
+  void transformExtensionList(List<Extension> nodes, TreeNode parent) {
+    transformList(nodes, parent, dummyExtension);
+  }
+
+  /// Transforms or removes [Constructor] nodes in [nodes] as children of
+  /// [parent].
+  ///
+  /// This is convenience method for calling [transformList] with removal
+  /// sentinel for [Constructor] nodes.
+  void transformConstructorList(List<Constructor> nodes, TreeNode parent) {
+    transformList(nodes, parent, dummyConstructor);
+  }
+
+  /// Transforms or removes [Procedure] nodes in [nodes] as children of
+  /// [parent].
+  ///
+  /// This is convenience method for calling [transformList] with removal
+  /// sentinel for [Procedure] nodes.
+  void transformProcedureList(List<Procedure> nodes, TreeNode parent) {
+    transformList(nodes, parent, dummyProcedure);
+  }
+
+  /// Transforms or removes [Field] nodes in [nodes] as children of [parent].
+  ///
+  /// This is convenience method for calling [transformList] with removal
+  /// sentinel for [Field] nodes.
+  void transformFieldList(List<Field> nodes, TreeNode parent) {
+    transformList(nodes, parent, dummyField);
+  }
+
+  /// Transforms or removes [RedirectingFactoryConstructor] nodes in [nodes] as
+  /// children of [parent].
+  ///
+  /// This is convenience method for calling [transformList] with removal
+  /// sentinel for [RedirectingFactoryConstructor] nodes.
+  void transformRedirectingFactoryConstructorList(
+      List<RedirectingFactoryConstructor> nodes, TreeNode parent) {
+    transformList(nodes, parent, dummyRedirectingFactoryConstructor);
+  }
+
+  /// Transforms or removes [Typedef] nodes in [nodes] as children of [parent].
+  ///
+  /// This is convenience method for calling [transformList] with removal
+  /// sentinel for [Typedef] nodes.
+  void transformTypedefList(List<Typedef> nodes, TreeNode parent) {
+    transformList(nodes, parent, dummyTypedef);
+  }
+
+  /// Transforms or removes [Initializer] nodes in [nodes] as children of
+  /// [parent].
+  ///
+  /// This is convenience method for calling [transformList] with removal
+  /// sentinel for [Initializer] nodes.
+  void transformInitializerList(List<Initializer> nodes, TreeNode parent) {
+    transformList(nodes, parent, dummyInitializer);
+  }
+
+  /// Transforms or removes [Expression] nodes in [nodes] as children of
+  /// [parent].
+  ///
+  /// This is convenience method for calling [transformList] with removal
+  /// sentinel for [Expression] nodes.
+  void transformExpressionList(List<Expression> nodes, TreeNode parent) {
+    transformList(nodes, parent, dummyExpression);
+  }
+
+  /// Transforms or removes [NamedExpression] nodes in [nodes] as children of
+  /// [parent].
+  ///
+  /// This is convenience method for calling [transformList] with removal
+  /// sentinel for [NamedExpression] nodes.
+  void transformNamedExpressionList(
+      List<NamedExpression> nodes, TreeNode parent) {
+    transformList(nodes, parent, dummyNamedExpression);
+  }
+
+  /// Transforms or removes [MapEntry] nodes in [nodes] as children of [parent].
+  ///
+  /// This is convenience method for calling [transformList] with removal
+  /// sentinel for [MapEntry] nodes.
+  void transformMapEntryList(List<MapEntry> nodes, TreeNode parent) {
+    transformList(nodes, parent, dummyMapEntry);
+  }
+
+  /// Transforms or removes [Statement] nodes in [nodes] as children of
+  /// [parent].
+  ///
+  /// This is convenience method for calling [transformList] with removal
+  /// sentinel for [Statement] nodes.
+  void transformStatementList(List<Statement> nodes, TreeNode parent) {
+    transformList(nodes, parent, dummyStatement);
+  }
+
+  /// Transforms or removes [SwitchCase] nodes in [nodes] as children of
+  /// [parent].
+  ///
+  /// This is convenience method for calling [transformList] with removal
+  /// sentinel for [SwitchCase] nodes.
+  void transformSwitchCaseList(List<SwitchCase> nodes, TreeNode parent) {
+    transformList(nodes, parent, dummySwitchCase);
+  }
+
+  /// Transforms or removes [Catch] nodes in [nodes] as children of [parent].
+  ///
+  /// This is convenience method for calling [transformList] with removal
+  /// sentinel for [Catch] nodes.
+  void transformCatchList(List<Catch> nodes, TreeNode parent) {
+    transformList(nodes, parent, dummyCatch);
+  }
+
+  /// Transforms or removes [TypeParameter] nodes in [nodes] as children of
+  /// [parent].
+  ///
+  /// This is convenience method for calling [transformList] with removal
+  /// sentinel for [TypeParameter] nodes.
+  void transformTypeParameterList(List<TypeParameter> nodes, TreeNode parent) {
+    transformList(nodes, parent, dummyTypeParameter);
+  }
+
+  /// Transforms or removes [VariableDeclaration] nodes in [nodes] as children
+  /// of [parent].
+  ///
+  /// This is convenience method for calling [transformList] with removal
+  /// sentinel for [VariableDeclaration] nodes.
+  void transformVariableDeclarationList(
+      List<VariableDeclaration> nodes, TreeNode parent) {
+    transformList(nodes, parent, dummyVariableDeclaration);
+  }
+
+  /// Transforms or removes [T] nodes in [nodes] as children of [parent] by
+  /// calling [transformOrRemove] using [removalSentinel] as the removal
+  /// sentinel.
+  void transformList<T extends TreeNode>(
+      List<T> nodes, TreeNode parent, T removalSentinel) {
+    int storeIndex = 0;
+    for (int i = 0; i < nodes.length; ++i) {
+      T? result = transformOrRemove(nodes[i], removalSentinel);
+      if (result != null) {
+        nodes[storeIndex] = result;
+        result.parent = parent;
+        ++storeIndex;
+      }
+    }
+    if (storeIndex < nodes.length) {
+      nodes.length = storeIndex;
+    }
+  }
+
+  /// Replaces a use of a type.
+  ///
+  /// By default, recursion stops at this point.
+  DartType visitDartType(DartType node, DartType? removalSentinel) => node;
+
+  Constant visitConstant(Constant node, Constant? removalSentinel) => node;
+
+  Supertype visitSupertype(Supertype node, Supertype? removalSentinel) => node;
+
+  TreeNode defaultTreeNode(TreeNode node, TreeNode? removalSentinel) {
+    node.transformOrRemoveChildren(this);
+    return node;
+  }
+}
+
 abstract class ExpressionVisitor1<R, T> {
   const ExpressionVisitor1();
 
diff --git a/pkg/kernel/test/verify_test.dart b/pkg/kernel/test/verify_test.dart
index 7b6a238..097200d 100644
--- a/pkg/kernel/test/verify_test.dart
+++ b/pkg/kernel/test/verify_test.dart
@@ -227,7 +227,7 @@
   negative1Test(
     'Dangling interface type',
     (TestHarness test) {
-      Class orphan = new Class();
+      Class orphan = new Class(name: 'Class');
       test.addNode(
           new TypeLiteral(new InterfaceType(orphan, Nullability.legacy)));
       return orphan;
@@ -264,27 +264,6 @@
       test.addNode(procedure);
     },
   );
-  simpleNegativeTest(
-    'StaticGet without target',
-    "StaticGet without target.",
-    (TestHarness test) {
-      test.addNode(StaticGet(null));
-    },
-  );
-  simpleNegativeTest(
-    'StaticSet without target',
-    "StaticSet without target.",
-    (TestHarness test) {
-      test.addNode(StaticSet(null, new NullLiteral()));
-    },
-  );
-  simpleNegativeTest(
-    'StaticInvocation without target',
-    "StaticInvocation without target.",
-    (TestHarness test) {
-      test.addNode(StaticInvocation(null, new Arguments.empty()));
-    },
-  );
   positiveTest(
     'Correct StaticInvocation',
     (TestHarness test) {
diff --git a/pkg/vm/lib/transformations/mixin_deduplication.dart b/pkg/vm/lib/transformations/mixin_deduplication.dart
index 31481a8..dce3d1b 100644
--- a/pkg/vm/lib/transformations/mixin_deduplication.dart
+++ b/pkg/vm/lib/transformations/mixin_deduplication.dart
@@ -2,6 +2,8 @@
 // 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.
 
+// @dart=2.12
+
 library vm.transformations.mixin_deduplication;
 
 import 'package:kernel/ast.dart';
@@ -13,7 +15,8 @@
 
   // Deduplicate mixins and re-resolve super initializers.
   // (this is a shallow transformation)
-  component.libraries.forEach(deduplicateMixins.visitLibrary);
+  component.libraries
+      .forEach((library) => deduplicateMixins.visitLibrary(library, null));
 
   // Do a deep transformation to update references to the removed mixin
   // application classes in the interface targets and types.
@@ -83,29 +86,30 @@
   }
 }
 
-class DeduplicateMixinsTransformer extends Transformer {
+class DeduplicateMixinsTransformer extends RemovingTransformer {
   final _canonicalMixins = new Map<_DeduplicateMixinKey, Class>();
   final _duplicatedMixins = new Map<Class, Class>();
 
   @override
-  TreeNode visitLibrary(Library node) {
-    transformList(node.classes, this, node);
+  TreeNode visitLibrary(Library node, TreeNode? removalSentinel) {
+    transformClassList(node.classes, node);
     return node;
   }
 
   @override
-  TreeNode visitClass(Class c) {
+  TreeNode visitClass(Class c, TreeNode? removalSentinel) {
     if (_duplicatedMixins.containsKey(c)) {
-      return null; // Class was de-duplicated already, just remove it.
+      // Class was de-duplicated already, just remove it.
+      return removalSentinel!;
     }
 
     if (c.supertype != null) {
-      c.supertype = _transformSupertype(c.supertype, c, true);
+      c.supertype = _transformSupertype(c.supertype!, c, true);
     }
     if (c.mixedInType != null) {
       throw 'All mixins should be transformed already.';
     }
-    transformSupertypeList(c.implementedTypes, this);
+    transformSupertypeList(c.implementedTypes);
 
     if (!c.isAnonymousMixin) {
       return c;
@@ -113,6 +117,7 @@
 
     Class canonical =
         _canonicalMixins.putIfAbsent(new _DeduplicateMixinKey(c), () => c);
+    // ignore: unnecessary_null_comparison
     assert(canonical != null);
 
     if (canonical != c) {
@@ -120,34 +125,34 @@
       // write a dangling reference to the deleted class.
       c.reference.canonicalName = null;
       _duplicatedMixins[c] = canonical;
-      return null; // Remove class.
+      // Remove class.
+      return removalSentinel!;
     }
 
     return c;
   }
 
   @override
-  Supertype visitSupertype(Supertype node) {
+  Supertype visitSupertype(Supertype node, Supertype? removalSentinel) {
     return _transformSupertype(node, null, false);
   }
 
   Supertype _transformSupertype(
-      Supertype supertype, Class cls, bool isSuperclass) {
+      Supertype supertype, Class? cls, bool isSuperclass) {
     Class oldSuper = supertype.classNode;
-    Class newSuper = visitClass(oldSuper);
-    if (newSuper == null) {
-      Class canonicalSuper = _duplicatedMixins[oldSuper];
-      assert(canonicalSuper != null);
+    Class newSuper = visitClass(oldSuper, dummyClass) as Class;
+    if (identical(newSuper, dummyClass)) {
+      Class canonicalSuper = _duplicatedMixins[oldSuper]!;
       supertype = new Supertype(canonicalSuper, supertype.typeArguments);
       if (isSuperclass) {
-        _correctForwardingConstructors(cls, oldSuper, canonicalSuper);
+        _correctForwardingConstructors(cls!, oldSuper, canonicalSuper);
       }
     }
     return supertype;
   }
 
   @override
-  TreeNode defaultTreeNode(TreeNode node) =>
+  TreeNode defaultTreeNode(TreeNode node, TreeNode? removalSentinel) =>
       throw 'Unexpected node ${node.runtimeType}: $node';
 }
 
@@ -204,17 +209,18 @@
 
   @override
   visitSuperMethodInvocation(SuperMethodInvocation node) {
-    node.interfaceTarget = _resolveNewInterfaceTarget(node.interfaceTarget);
+    node.interfaceTarget =
+        _resolveNewInterfaceTarget(node.interfaceTarget) as Procedure?;
     super.visitSuperMethodInvocation(node);
   }
 
-  Member _resolveNewInterfaceTarget(Member m) {
-    final Class c = m?.enclosingClass;
+  Member? _resolveNewInterfaceTarget(Member? m) {
+    final Class? c = m?.enclosingClass;
     if (c != null && c.isAnonymousMixin) {
-      final Class replacement = transformer._duplicatedMixins[c];
+      final Class? replacement = transformer._duplicatedMixins[c];
       if (replacement != null) {
         // The class got removed, so we need to re-resolve the interface target.
-        return _findMember(replacement, m);
+        return _findMember(replacement, m!);
       }
     }
     return m;
@@ -240,8 +246,8 @@
 
   Reference _updateClassReference(Reference classRef) {
     final Class c = classRef.asClass;
-    if (c != null && c.isAnonymousMixin) {
-      final Class replacement = transformer._duplicatedMixins[c];
+    if (c.isAnonymousMixin) {
+      final Class? replacement = transformer._duplicatedMixins[c];
       if (replacement != null) {
         return replacement.reference;
       }
@@ -279,7 +285,7 @@
     for (var initializer in constructor.initializers) {
       if ((initializer is SuperInitializer) &&
           initializer.target.enclosingClass == oldSuper) {
-        Constructor replacement = null;
+        Constructor? replacement = null;
         for (var c in newSuper.constructors) {
           if (c.name == initializer.target.name) {
             replacement = c;
diff --git a/pkg/vm/lib/transformations/type_flow/transformer.dart b/pkg/vm/lib/transformations/type_flow/transformer.dart
index ba36509..ac88a41 100644
--- a/pkg/vm/lib/transformations/type_flow/transformer.dart
+++ b/pkg/vm/lib/transformations/type_flow/transformer.dart
@@ -523,8 +523,8 @@
   }
 
   transformComponent(Component component) {
-    _pass1.transform(component);
-    _pass2.transform(component);
+    _pass1.transformComponent(component);
+    _pass2.transformComponent(component);
   }
 
   bool isClassReferencedFromNativeCode(Class c) =>
@@ -563,8 +563,8 @@
       }
       _usedClasses.add(c);
       visitIterable(c.supers, typeVisitor);
-      transformList(c.typeParameters, _pass1, c);
-      transformList(c.annotations, _pass1, c);
+      _pass1.transformTypeParameterList(c.typeParameters, c);
+      _pass1.transformExpressionList(c.annotations, c);
       // Preserve NSM forwarders. They are overlooked by TFA / tree shaker
       // as they are abstract and don't have a body.
       for (Procedure p in c.procedures) {
@@ -615,13 +615,14 @@
       }
 
       if (func != null) {
-        transformList(func.typeParameters, _pass1, func);
-        transformList(func.positionalParameters, _pass1, func);
-        transformList(func.namedParameters, _pass1, func);
+        _pass1.transformTypeParameterList(func.typeParameters, func);
+        _pass1.transformVariableDeclarationList(
+            func.positionalParameters, func);
+        _pass1.transformVariableDeclarationList(func.namedParameters, func);
         func.returnType.accept(typeVisitor);
       }
 
-      transformList(m.annotations, _pass1, m);
+      _pass1.transformExpressionList(m.annotations, m);
 
       // If the member is kept alive we need to keep the extension alive.
       if (m.isExtensionMember) {
@@ -641,18 +642,20 @@
 
   void addUsedExtension(Extension node) {
     if (_usedExtensions.add(node)) {
-      transformList(node.typeParameters, _pass1, node);
+      _pass1.transformTypeParameterList(node.typeParameters, node);
       node.onType?.accept(typeVisitor);
     }
   }
 
   void addUsedTypedef(Typedef typedef) {
     if (_usedTypedefs.add(typedef)) {
-      transformList(typedef.annotations, _pass1, typedef);
-      transformList(typedef.typeParameters, _pass1, typedef);
-      transformList(typedef.typeParametersOfFunctionType, _pass1, typedef);
-      transformList(typedef.positionalParameters, _pass1, typedef);
-      transformList(typedef.namedParameters, _pass1, typedef);
+      _pass1.transformExpressionList(typedef.annotations, typedef);
+      _pass1.transformTypeParameterList(typedef.typeParameters, typedef);
+      _pass1.transformTypeParameterList(
+          typedef.typeParametersOfFunctionType, typedef);
+      _pass1.transformVariableDeclarationList(
+          typedef.positionalParameters, typedef);
+      _pass1.transformVariableDeclarationList(typedef.namedParameters, typedef);
       typedef.type?.accept(typeVisitor);
     }
   }
@@ -786,7 +789,7 @@
 /// Visits all classes, members and bodies of reachable members.
 /// Collects all used classes, members and types, and
 /// transforms unreachable calls into 'throw' expressions.
-class _TreeShakerPass1 extends Transformer {
+class _TreeShakerPass1 extends RemovingTransformer {
   final TreeShaker shaker;
   final FieldMorpher fieldMorpher;
   final TypeEnvironment environment;
@@ -808,8 +811,8 @@
       : fieldMorpher = shaker.fieldMorpher,
         environment = shaker.typeFlowAnalysis.environment;
 
-  void transform(Component component) {
-    component.transformChildren(this);
+  void transformComponent(Component component) {
+    component.transformOrRemoveChildren(this);
   }
 
   bool _isUnreachable(TreeNode node) {
@@ -870,61 +873,62 @@
   NarrowNotNull _getNullTest(TreeNode node) =>
       shaker.typeFlowAnalysis.nullTest(node);
 
-  TreeNode _visitAssertNode(TreeNode node) {
+  TreeNode _visitAssertNode(TreeNode node, TreeNode removalSentinel) {
     if (kRemoveAsserts) {
-      return null;
+      return removalSentinel;
     } else {
-      node.transformChildren(this);
+      node.transformOrRemoveChildren(this);
       return node;
     }
   }
 
   @override
-  DartType visitDartType(DartType node) {
+  DartType visitDartType(DartType node, DartType removalSentinel) {
     node.accept(shaker.typeVisitor);
     return node;
   }
 
   @override
-  Supertype visitSupertype(Supertype node) {
+  Supertype visitSupertype(Supertype node, Supertype removalSentinel) {
     node.accept(shaker.typeVisitor);
     return node;
   }
 
   @override
-  TreeNode visitTypedef(Typedef node) {
+  TreeNode visitTypedef(Typedef node, TreeNode removalSentinel) {
     return node; // Do not go deeper.
   }
 
   @override
-  Extension visitExtension(Extension node) {
+  Extension visitExtension(Extension node, TreeNode removalSentinel) {
     // The extension can be considered a weak node, we'll only retain it if
     // normal code references any of it's members.
     return node;
   }
 
   @override
-  TreeNode visitClass(Class node) {
+  TreeNode visitClass(Class node, TreeNode removalSentinel) {
     if (shaker.isClassAllocated(node) ||
         shaker.isClassReferencedFromNativeCode(node)) {
       shaker.addClassUsedInType(node);
     }
-    transformList(node.constructors, this, node);
-    transformList(node.procedures, this, node);
-    transformList(node.fields, this, node);
-    transformList(node.redirectingFactoryConstructors, this, node);
+    transformConstructorList(node.constructors, node);
+    transformProcedureList(node.procedures, node);
+    transformFieldList(node.fields, node);
+    transformRedirectingFactoryConstructorList(
+        node.redirectingFactoryConstructors, node);
     return node;
   }
 
   @override
-  TreeNode defaultMember(Member node) {
+  TreeNode defaultMember(Member node, TreeNode removalSentinel) {
     currentMember = node;
     if (shaker.isMemberBodyReachable(node)) {
       if (kPrintTrace) {
         tracePrint("Visiting $node");
       }
       shaker.addUsedMember(node);
-      node.transformChildren(this);
+      node.transformOrRemoveChildren(this);
     } else if (shaker.isMemberReferencedFromNativeCode(node)) {
       // Preserve members referenced from native code to satisfy lookups, even
       // if they are not reachable. An instance member could be added via
@@ -937,7 +941,7 @@
   }
 
   @override
-  TreeNode visitField(Field node) {
+  TreeNode visitField(Field node, TreeNode removalSentinel) {
     currentMember = node;
     if (shaker.retainField(node)) {
       if (kPrintTrace) {
@@ -946,7 +950,7 @@
       shaker.addUsedMember(node);
       if (node.initializer != null) {
         if (shaker.isFieldInitializerReachable(node)) {
-          node.transformChildren(this);
+          node.transformOrRemoveChildren(this);
         } else {
           node.initializer = _makeUnreachableCall([])..parent = node;
         }
@@ -961,8 +965,9 @@
   }
 
   @override
-  TreeNode visitMethodInvocation(MethodInvocation node) {
-    node.transformChildren(this);
+  TreeNode visitMethodInvocation(
+      MethodInvocation node, TreeNode removalSentinel) {
+    node.transformOrRemoveChildren(this);
     if (_isUnreachable(node)) {
       return _makeUnreachableCall(
           _flattenArguments(node.arguments, receiver: node.receiver));
@@ -984,8 +989,8 @@
   }
 
   @override
-  TreeNode visitPropertyGet(PropertyGet node) {
-    node.transformChildren(this);
+  TreeNode visitPropertyGet(PropertyGet node, TreeNode removalSentinel) {
+    node.transformOrRemoveChildren(this);
     if (_isUnreachable(node)) {
       return _makeUnreachableCall([node.receiver]);
     } else {
@@ -999,8 +1004,8 @@
   }
 
   @override
-  TreeNode visitPropertySet(PropertySet node) {
-    node.transformChildren(this);
+  TreeNode visitPropertySet(PropertySet node, TreeNode removalSentinel) {
+    node.transformOrRemoveChildren(this);
     if (_isUnreachable(node)) {
       return _makeUnreachableCall([node.receiver, node.value]);
     } else {
@@ -1014,8 +1019,9 @@
   }
 
   @override
-  TreeNode visitSuperMethodInvocation(SuperMethodInvocation node) {
-    node.transformChildren(this);
+  TreeNode visitSuperMethodInvocation(
+      SuperMethodInvocation node, TreeNode removalSentinel) {
+    node.transformOrRemoveChildren(this);
     if (_isUnreachable(node)) {
       return _makeUnreachableCall(_flattenArguments(node.arguments));
     } else {
@@ -1029,8 +1035,9 @@
   }
 
   @override
-  TreeNode visitSuperPropertyGet(SuperPropertyGet node) {
-    node.transformChildren(this);
+  TreeNode visitSuperPropertyGet(
+      SuperPropertyGet node, TreeNode removalSentinel) {
+    node.transformOrRemoveChildren(this);
     if (_isUnreachable(node)) {
       return _makeUnreachableCall([]);
     } else {
@@ -1044,8 +1051,9 @@
   }
 
   @override
-  TreeNode visitSuperPropertySet(SuperPropertySet node) {
-    node.transformChildren(this);
+  TreeNode visitSuperPropertySet(
+      SuperPropertySet node, TreeNode removalSentinel) {
+    node.transformOrRemoveChildren(this);
     if (_isUnreachable(node)) {
       return _makeUnreachableCall([node.value]);
     } else {
@@ -1059,8 +1067,9 @@
   }
 
   @override
-  TreeNode visitStaticInvocation(StaticInvocation node) {
-    node.transformChildren(this);
+  TreeNode visitStaticInvocation(
+      StaticInvocation node, TreeNode removalSentinel) {
+    node.transformOrRemoveChildren(this);
     if (_isUnreachable(node)) {
       return _makeUnreachableCall(_flattenArguments(node.arguments));
     }
@@ -1073,8 +1082,8 @@
   }
 
   @override
-  TreeNode visitStaticGet(StaticGet node) {
-    node.transformChildren(this);
+  TreeNode visitStaticGet(StaticGet node, TreeNode removalSentinel) {
+    node.transformOrRemoveChildren(this);
     if (_isUnreachable(node)) {
       return _makeUnreachableCall([]);
     } else {
@@ -1088,14 +1097,14 @@
   }
 
   @override
-  Constant visitConstant(Constant node) {
+  Constant visitConstant(Constant node, Constant removalSentinel) {
     shaker.constantVisitor.analyzeConstant(node);
     return node;
   }
 
   @override
-  TreeNode visitStaticSet(StaticSet node) {
-    node.transformChildren(this);
+  TreeNode visitStaticSet(StaticSet node, TreeNode removalSentinel) {
+    node.transformOrRemoveChildren(this);
     if (_isUnreachable(node)) {
       return _makeUnreachableCall([node.value]);
     } else {
@@ -1110,8 +1119,9 @@
   }
 
   @override
-  TreeNode visitConstructorInvocation(ConstructorInvocation node) {
-    node.transformChildren(this);
+  TreeNode visitConstructorInvocation(
+      ConstructorInvocation node, TreeNode removalSentinel) {
+    node.transformOrRemoveChildren(this);
     if (_isUnreachable(node)) {
       return _makeUnreachableCall(_flattenArguments(node.arguments));
     } else {
@@ -1125,8 +1135,9 @@
   }
 
   @override
-  TreeNode visitRedirectingInitializer(RedirectingInitializer node) {
-    node.transformChildren(this);
+  TreeNode visitRedirectingInitializer(
+      RedirectingInitializer node, TreeNode removalSentinel) {
+    node.transformOrRemoveChildren(this);
     if (_isUnreachable(node)) {
       return _makeUnreachableInitializer(_flattenArguments(node.arguments));
     } else {
@@ -1137,8 +1148,9 @@
   }
 
   @override
-  TreeNode visitSuperInitializer(SuperInitializer node) {
-    node.transformChildren(this);
+  TreeNode visitSuperInitializer(
+      SuperInitializer node, TreeNode removalSentinel) {
+    node.transformOrRemoveChildren(this);
     if (_isUnreachable(node)) {
       return _makeUnreachableInitializer(_flattenArguments(node.arguments));
     } else {
@@ -1148,8 +1160,9 @@
   }
 
   @override
-  TreeNode visitFieldInitializer(FieldInitializer node) {
-    node.transformChildren(this);
+  TreeNode visitFieldInitializer(
+      FieldInitializer node, TreeNode removalSentinel) {
+    node.transformOrRemoveChildren(this);
     if (_isUnreachable(node)) {
       return _makeUnreachableInitializer([node.value]);
     } else {
@@ -1160,7 +1173,7 @@
           return LocalInitializer(
               VariableDeclaration(null, initializer: node.value));
         } else {
-          return null;
+          return removalSentinel;
         }
       }
       return node;
@@ -1168,23 +1181,25 @@
   }
 
   @override
-  TreeNode visitAssertStatement(AssertStatement node) {
-    return _visitAssertNode(node);
+  TreeNode visitAssertStatement(
+      AssertStatement node, TreeNode removalSentinel) {
+    return _visitAssertNode(node, removalSentinel);
   }
 
   @override
-  TreeNode visitAssertBlock(AssertBlock node) {
-    return _visitAssertNode(node);
+  TreeNode visitAssertBlock(AssertBlock node, TreeNode removalSentinel) {
+    return _visitAssertNode(node, removalSentinel);
   }
 
   @override
-  TreeNode visitAssertInitializer(AssertInitializer node) {
-    return _visitAssertNode(node);
+  TreeNode visitAssertInitializer(
+      AssertInitializer node, TreeNode removalSentinel) {
+    return _visitAssertNode(node, removalSentinel);
   }
 
   @override
-  TreeNode visitAsExpression(AsExpression node) {
-    node.transformChildren(this);
+  TreeNode visitAsExpression(AsExpression node, TreeNode removalSentinel) {
+    node.transformOrRemoveChildren(this);
     TypeCheck check = shaker.typeFlowAnalysis.explicitCast(node);
     if (check != null && check.canAlwaysSkip) {
       return StaticInvocation(
@@ -1195,8 +1210,8 @@
   }
 
   @override
-  TreeNode visitNullCheck(NullCheck node) {
-    node.transformChildren(this);
+  TreeNode visitNullCheck(NullCheck node, TreeNode removalSentinel) {
+    node.transformOrRemoveChildren(this);
     final nullTest = _getNullTest(node);
     if (nullTest.isAlwaysNotNull) {
       return StaticInvocation(
@@ -1221,13 +1236,13 @@
 /// This pass visits classes and members and removes unused classes and members.
 /// Bodies of unreachable but used members are replaced with 'throw'
 /// expressions. This pass does not dive deeper than member level.
-class _TreeShakerPass2 extends Transformer {
+class _TreeShakerPass2 extends RemovingTransformer {
   final TreeShaker shaker;
 
   _TreeShakerPass2(this.shaker);
 
-  void transform(Component component) {
-    component.transformChildren(this);
+  void transformComponent(Component component) {
+    component.transformOrRemoveChildren(this);
     for (Source source in component.uriToSource.values) {
       source?.constantCoverageConstructors?.removeWhere((Reference reference) {
         Member node = reference.asMember;
@@ -1237,8 +1252,8 @@
   }
 
   @override
-  TreeNode visitLibrary(Library node) {
-    node.transformChildren(this);
+  TreeNode visitLibrary(Library node, TreeNode removalSentinel) {
+    node.transformOrRemoveChildren(this);
     // The transformer API does not iterate over `Library.additionalExports`,
     // so we manually delete the references to shaken nodes.
     node.additionalExports.removeWhere((Reference reference) {
@@ -1257,19 +1272,19 @@
   }
 
   @override
-  Typedef visitTypedef(Typedef node) {
-    return shaker.isTypedefUsed(node) ? node : null;
+  Typedef visitTypedef(Typedef node, TreeNode removalSentinel) {
+    return shaker.isTypedefUsed(node) ? node : removalSentinel;
   }
 
   @override
-  Class visitClass(Class node) {
+  Class visitClass(Class node, TreeNode removalSentinel) {
     if (!shaker.isClassUsed(node)) {
       debugPrint('Dropped class ${node.name}');
       // Ensure that kernel file writer will not be able to
       // write a dangling reference to the deleted class.
       node.reference.canonicalName = null;
       Statistics.classesDropped++;
-      return null; // Remove the class.
+      return removalSentinel; // Remove the class.
     }
 
     if (!shaker.isClassUsedInType(node)) {
@@ -1292,7 +1307,7 @@
       node.isAbstract = true;
     }
 
-    node.transformChildren(this);
+    node.transformOrRemoveChildren(this);
 
     return node;
   }
@@ -1305,13 +1320,13 @@
       node.enclosingClass.isEnum;
 
   @override
-  Member defaultMember(Member node) {
+  Member defaultMember(Member node, TreeNode removalSentinel) {
     if (!shaker.isMemberUsed(node) && !_preserveSpecialMember(node)) {
       // Ensure that kernel file writer will not be able to
       // write a dangling reference to the deleted member.
       node.reference.canonicalName = null;
       Statistics.membersDropped++;
-      return null;
+      return removalSentinel;
     }
 
     if (!shaker.isMemberBodyReachable(node)) {
@@ -1358,7 +1373,7 @@
   }
 
   @override
-  Extension visitExtension(Extension node) {
+  Extension visitExtension(Extension node, TreeNode removalSentinel) {
     if (shaker.isExtensionUsed(node)) {
       int writeIndex = 0;
       for (int i = 0; i < node.members.length; ++i) {
@@ -1379,7 +1394,7 @@
       assert(node.members.length > 0);
       return node;
     }
-    return null;
+    return removalSentinel;
   }
 
   void _makeUnreachableBody(FunctionNode function) {
@@ -1391,7 +1406,7 @@
   }
 
   @override
-  TreeNode defaultTreeNode(TreeNode node) {
+  TreeNode defaultTreeNode(TreeNode node, TreeNode removalSentinel) {
     return node; // Do not traverse into other nodes.
   }
 }
diff --git a/pkg/vm/lib/transformations/unreachable_code_elimination.dart b/pkg/vm/lib/transformations/unreachable_code_elimination.dart
index 8444ce7..10bd958 100644
--- a/pkg/vm/lib/transformations/unreachable_code_elimination.dart
+++ b/pkg/vm/lib/transformations/unreachable_code_elimination.dart
@@ -2,17 +2,20 @@
 // 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.
 
+// @dart=2.12
+
 import 'package:kernel/ast.dart';
 
 /// Simple unreachable code elimination: removes asserts and if statements
 /// with constant conditions. Does a very limited constant folding of
 /// logical expressions.
 Component transformComponent(Component component, bool enableAsserts) {
-  new SimpleUnreachableCodeElimination(enableAsserts).visitComponent(component);
+  new SimpleUnreachableCodeElimination(enableAsserts)
+      .visitComponent(component, null);
   return component;
 }
 
-class SimpleUnreachableCodeElimination extends Transformer {
+class SimpleUnreachableCodeElimination extends RemovingTransformer {
   final bool enableAsserts;
 
   SimpleUnreachableCodeElimination(this.enableAsserts);
@@ -37,24 +40,27 @@
   Expression _createBoolLiteral(bool value, int fileOffset) =>
       new BoolLiteral(value)..fileOffset = fileOffset;
 
-  Statement _makeEmptyBlockIfNull(Statement node, TreeNode parent) =>
-      node == null ? (Block(<Statement>[])..parent = parent) : node;
+  Statement _makeEmptyBlockIfEmptyStatement(Statement node, TreeNode parent) =>
+      node is EmptyStatement ? (Block(<Statement>[])..parent = parent) : node;
 
   @override
-  TreeNode visitIfStatement(IfStatement node) {
-    node.transformChildren(this);
+  TreeNode visitIfStatement(IfStatement node, TreeNode? removalSentinel) {
+    node.transformOrRemoveChildren(this);
     final condition = node.condition;
     if (_isBoolConstant(condition)) {
       final value = _getBoolConstantValue(condition);
-      return value ? node.then : node.otherwise;
+      return value
+          ? node.then
+          : (node.otherwise ?? removalSentinel ?? new EmptyStatement());
     }
-    node.then = _makeEmptyBlockIfNull(node.then, node);
+    node.then = _makeEmptyBlockIfEmptyStatement(node.then, node);
     return node;
   }
 
   @override
-  visitConditionalExpression(ConditionalExpression node) {
-    node.transformChildren(this);
+  visitConditionalExpression(
+      ConditionalExpression node, TreeNode? removalSentinel) {
+    node.transformOrRemoveChildren(this);
     final condition = node.condition;
     if (_isBoolConstant(condition)) {
       final value = _getBoolConstantValue(condition);
@@ -64,8 +70,8 @@
   }
 
   @override
-  TreeNode visitNot(Not node) {
-    node.transformChildren(this);
+  TreeNode visitNot(Not node, TreeNode? removalSentinel) {
+    node.transformOrRemoveChildren(this);
     final operand = node.operand;
     if (_isBoolConstant(operand)) {
       return _createBoolLiteral(
@@ -75,8 +81,9 @@
   }
 
   @override
-  TreeNode visitLogicalExpression(LogicalExpression node) {
-    node.transformChildren(this);
+  TreeNode visitLogicalExpression(
+      LogicalExpression node, TreeNode? removalSentinel) {
+    node.transformOrRemoveChildren(this);
     final left = node.left;
     final right = node.right;
     final operatorEnum = node.operatorEnum;
@@ -104,8 +111,8 @@
   }
 
   @override
-  visitStaticGet(StaticGet node) {
-    node.transformChildren(this);
+  visitStaticGet(StaticGet node, TreeNode? removalSentinel) {
+    node.transformOrRemoveChildren(this);
     final target = node.target;
     if (target is Field && target.isConst) {
       throw 'StaticGet from const field $target should be evaluated by front-end: $node';
@@ -114,34 +121,38 @@
   }
 
   @override
-  TreeNode visitAssertStatement(AssertStatement node) {
+  TreeNode visitAssertStatement(
+      AssertStatement node, TreeNode? removalSentinel) {
     if (!enableAsserts) {
-      return null;
+      return removalSentinel ?? new EmptyStatement();
     }
-    return super.visitAssertStatement(node);
+    return super.visitAssertStatement(node, removalSentinel);
   }
 
   @override
-  TreeNode visitAssertBlock(AssertBlock node) {
+  TreeNode visitAssertBlock(AssertBlock node, TreeNode? removalSentinel) {
     if (!enableAsserts) {
-      return null;
+      return removalSentinel ?? new EmptyStatement();
     }
-    return super.visitAssertBlock(node);
+    return super.visitAssertBlock(node, removalSentinel);
   }
 
   @override
-  TreeNode visitAssertInitializer(AssertInitializer node) {
+  TreeNode visitAssertInitializer(
+      AssertInitializer node, TreeNode? removalSentinel) {
     if (!enableAsserts) {
-      return null;
+      // Initializers only occur in the initializer list where they are always
+      // removable.
+      return removalSentinel!;
     }
-    return super.visitAssertInitializer(node);
+    return super.visitAssertInitializer(node, removalSentinel);
   }
 
   @override
-  TreeNode visitTryFinally(TryFinally node) {
-    node.transformChildren(this);
+  TreeNode visitTryFinally(TryFinally node, TreeNode? removalSentinel) {
+    node.transformOrRemoveChildren(this);
     final fin = node.finalizer;
-    if (fin == null || (fin is Block && fin.statements.isEmpty)) {
+    if (fin is EmptyStatement || (fin is Block && fin.statements.isEmpty)) {
       return node.body;
     }
     return node;
@@ -157,8 +168,8 @@
   }
 
   @override
-  TreeNode visitTryCatch(TryCatch node) {
-    node.transformChildren(this);
+  TreeNode visitTryCatch(TryCatch node, TreeNode? removalSentinel) {
+    node.transformOrRemoveChildren(this);
     // Can replace try/catch with its body if all catches are just rethow.
     for (Catch catchClause in node.catches) {
       if (!_isRethrow(catchClause.body)) {
@@ -174,30 +185,30 @@
   // need to guard against null.
 
   @override
-  TreeNode visitWhileStatement(WhileStatement node) {
-    node.transformChildren(this);
-    node.body = _makeEmptyBlockIfNull(node.body, node);
+  TreeNode visitWhileStatement(WhileStatement node, TreeNode? removalSentinel) {
+    node.transformOrRemoveChildren(this);
+    node.body = _makeEmptyBlockIfEmptyStatement(node.body, node);
     return node;
   }
 
   @override
-  TreeNode visitDoStatement(DoStatement node) {
-    node.transformChildren(this);
-    node.body = _makeEmptyBlockIfNull(node.body, node);
+  TreeNode visitDoStatement(DoStatement node, TreeNode? removalSentinel) {
+    node.transformOrRemoveChildren(this);
+    node.body = _makeEmptyBlockIfEmptyStatement(node.body, node);
     return node;
   }
 
   @override
-  TreeNode visitForStatement(ForStatement node) {
-    node.transformChildren(this);
-    node.body = _makeEmptyBlockIfNull(node.body, node);
+  TreeNode visitForStatement(ForStatement node, TreeNode? removalSentinel) {
+    node.transformOrRemoveChildren(this);
+    node.body = _makeEmptyBlockIfEmptyStatement(node.body, node);
     return node;
   }
 
   @override
-  TreeNode visitForInStatement(ForInStatement node) {
-    node.transformChildren(this);
-    node.body = _makeEmptyBlockIfNull(node.body, node);
+  TreeNode visitForInStatement(ForInStatement node, TreeNode? removalSentinel) {
+    node.transformOrRemoveChildren(this);
+    node.body = _makeEmptyBlockIfEmptyStatement(node.body, node);
     return node;
   }
 }