Version 2.13.0-214.0.dev

Merge commit '05c70ab355a256fb45672dfb4644792414b29c0d' into 'dev'
diff --git a/pkg/analysis_server/lib/src/services/kythe/kythe_visitors.dart b/pkg/analysis_server/lib/src/services/kythe/kythe_visitors.dart
index 57085d1..f9c3c66 100644
--- a/pkg/analysis_server/lib/src/services/kythe/kythe_visitors.dart
+++ b/pkg/analysis_server/lib/src/services/kythe/kythe_visitors.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
-
 import 'dart:convert';
 
 import 'package:analyzer/dart/ast/ast.dart';
@@ -14,6 +12,7 @@
 import 'package:analyzer/dart/element/type.dart';
 import 'package:analyzer/dart/element/visitor.dart';
 import 'package:analyzer/file_system/file_system.dart';
+import 'package:analyzer/src/dart/ast/extensions.dart';
 import 'package:analyzer/src/dart/element/inheritance_manager3.dart';
 import 'package:analyzer/src/workspace/bazel.dart';
 import 'package:analyzer/src/workspace/gn.dart';
@@ -28,7 +27,6 @@
 /// name of the constructor, unless the constructor is a named constructor in
 /// which '<class-name>.<constructor-name>' is returned.
 String _computeConstructorElementName(ConstructorElement element) {
-  assert(element != null);
   var name = element.enclosingElement.name;
   var constructorName = element.name;
   if (constructorName.isNotEmpty) {
@@ -42,17 +40,18 @@
   return '$start-$end';
 }
 
-String _getPath(ResourceProvider provider, Element e) {
+String _getPath(ResourceProvider provider, Element? e) {
   // TODO(jwren) This method simply serves to provide the WORKSPACE relative
   // path for sources in Elements, it needs to be written in a more robust way.
   // TODO(jwren) figure out what source generates a e != null, but
   // e.source == null to ensure that it is not a bug somewhere in the stack.
-  if (e == null || e.source == null) {
+  var source = e?.source;
+  if (source == null) {
     // null sometimes when the element is used to generate the node type
     // "dynamic"
     return '';
   }
-  var path = e.source.fullName;
+  var path = source.fullName;
   var bazelWorkspace = BazelWorkspace.find(provider, path);
   if (bazelWorkspace != null) {
     return provider.pathContext.relative(path, from: bazelWorkspace.root);
@@ -70,7 +69,7 @@
 /// If a non-null element is passed, the [SignatureElementVisitor] is used to
 /// generate and return a [String] signature, otherwise [schema.DYNAMIC_KIND] is
 /// returned.
-String _getSignature(ResourceProvider provider, Element element,
+String _getSignature(ResourceProvider provider, Element? element,
     String nodeKind, String corpus) {
   assert(nodeKind != schema.ANCHOR_KIND); // Call _getAnchorSignature instead
   if (element == null) {
@@ -95,12 +94,12 @@
   final InheritanceManager3 _inheritanceManager;
   final String _contents;
 
-  String _enclosingFilePath = '';
-  Element _enclosingElement;
-  ClassElement _enclosingClassElement;
-  KytheVName _enclosingVName;
-  KytheVName _enclosingFileVName;
-  KytheVName _enclosingClassVName;
+  late String _enclosingFilePath = '';
+  Element? _enclosingElement;
+  ClassElement? _enclosingClassElement;
+  KytheVName? _enclosingVName;
+  KytheVName? _enclosingFileVName;
+  KytheVName? _enclosingClassVName;
 
   KytheDartVisitor(this.resourceProvider, this.entries, this.corpus,
       this._inheritanceManager, this._contents);
@@ -115,9 +114,12 @@
 
     var start = node.name.offset;
     var end = node.name.end;
-    if (node.constructorName != null) {
-      end = node.constructorName.end;
+
+    var constructorName = node.constructorName;
+    if (constructorName != null) {
+      end = constructorName.end;
     }
+
     var refVName = _handleRefEdge(
       node.element,
       const <String>[schema.REF_EDGE],
@@ -135,9 +137,11 @@
           _handleVariableDeclarationListAnnotations(
               parentNode.fields, refVName);
         } else if (parentElement != null) {
-          var parentVName =
-              _vNameFromElement(parentElement, _getNodeKind(parentElement));
-          addEdge(parentVName, schema.ANNOTATED_BY_EDGE, refVName);
+          var nodeKind = _getNodeKind(parentElement);
+          if (nodeKind != null) {
+            var parentVName = _vNameFromElement(parentElement, nodeKind);
+            addEdge(parentVName, schema.ANNOTATED_BY_EDGE, refVName);
+          }
         } else {
           // parentAstNode is not a variable declaration node and
           // parentElement == null
@@ -202,7 +206,7 @@
 
   @override
   void visitClassDeclaration(ClassDeclaration node) {
-    return _withEnclosingElement(node.declaredElement, () {
+    return _withEnclosingElement(node.declaredElement!, () {
       // record/ class node
       addNodeAndFacts(schema.RECORD_KIND,
           nodeVName: _enclosingClassVName,
@@ -227,33 +231,30 @@
           target: _enclosingClassVName);
 
       // extends
-      var supertype = _enclosingClassElement.supertype;
-      if (supertype?.element != null) {
+      var supertype = _enclosingClassElement!.supertype;
+      var supertypeElement = supertype?.element;
+      if (supertypeElement != null) {
         var recordSupertypeVName =
-            _vNameFromElement(supertype.element, schema.RECORD_KIND);
+            _vNameFromElement(supertypeElement, schema.RECORD_KIND);
         addEdge(
-            _enclosingClassVName, schema.EXTENDS_EDGE, recordSupertypeVName);
+            _enclosingClassVName!, schema.EXTENDS_EDGE, recordSupertypeVName);
       }
 
       // implements
-      var interfaces = _enclosingClassElement.interfaces;
+      var interfaces = _enclosingClassElement!.interfaces;
       for (var interface in interfaces) {
-        if (interface.element != null) {
-          var recordInterfaceVName =
-              _vNameFromElement(interface.element, schema.RECORD_KIND);
-          addEdge(
-              _enclosingClassVName, schema.EXTENDS_EDGE, recordInterfaceVName);
-        }
+        var recordInterfaceVName =
+            _vNameFromElement(interface.element, schema.RECORD_KIND);
+        addEdge(
+            _enclosingClassVName!, schema.EXTENDS_EDGE, recordInterfaceVName);
       }
 
       // mixins
-      var mixins = _enclosingClassElement.mixins;
+      var mixins = _enclosingClassElement!.mixins;
       for (var mixin in mixins) {
-        if (mixin.element != null) {
-          var recordMixinVName =
-              _vNameFromElement(mixin.element, schema.RECORD_KIND);
-          addEdge(_enclosingClassVName, schema.EXTENDS_EDGE, recordMixinVName);
-        }
+        var recordMixinVName =
+            _vNameFromElement(mixin.element, schema.RECORD_KIND);
+        addEdge(_enclosingClassVName!, schema.EXTENDS_EDGE, recordMixinVName);
       }
 
       // TODO (jwren) type parameters
@@ -272,7 +273,7 @@
 
   @override
   void visitClassTypeAlias(ClassTypeAlias node) {
-    return _withEnclosingElement(node.declaredElement, () {
+    return _withEnclosingElement(node.declaredElement!, () {
       // record/ class node
       addNodeAndFacts(schema.RECORD_KIND,
           nodeVName: _enclosingClassVName,
@@ -303,27 +304,23 @@
       // extends
       var recordSupertypeVName = _vNameFromElement(
           node.superclass.name.staticElement, schema.RECORD_KIND);
-      addEdge(_enclosingClassVName, schema.EXTENDS_EDGE, recordSupertypeVName);
+      addEdge(_enclosingClassVName!, schema.EXTENDS_EDGE, recordSupertypeVName);
 
       // implements
-      var interfaces = _enclosingClassElement.interfaces;
+      var interfaces = _enclosingClassElement!.interfaces;
       for (var interface in interfaces) {
-        if (interface.element != null) {
-          var recordInterfaceVName =
-              _vNameFromElement(interface.element, schema.RECORD_KIND);
-          addEdge(
-              _enclosingClassVName, schema.EXTENDS_EDGE, recordInterfaceVName);
-        }
+        var recordInterfaceVName =
+            _vNameFromElement(interface.element, schema.RECORD_KIND);
+        addEdge(
+            _enclosingClassVName!, schema.EXTENDS_EDGE, recordInterfaceVName);
       }
 
       // mixins
-      var mixins = _enclosingClassElement.mixins;
+      var mixins = _enclosingClassElement!.mixins;
       for (var mixin in mixins) {
-        if (mixin.element != null) {
-          var recordMixinVName =
-              _vNameFromElement(mixin.element, schema.RECORD_KIND);
-          addEdge(_enclosingClassVName, schema.EXTENDS_EDGE, recordMixinVName);
-        }
+        var recordMixinVName =
+            _vNameFromElement(mixin.element, schema.RECORD_KIND);
+        addEdge(_enclosingClassVName!, schema.EXTENDS_EDGE, recordMixinVName);
       }
 
       // visit children
@@ -337,12 +334,13 @@
 
   @override
   void visitCompilationUnit(CompilationUnit node) {
-    _enclosingFilePath = _getPath(resourceProvider, node.declaredElement);
-    return _withEnclosingElement(node.declaredElement, () {
-      addFact(_enclosingFileVName, schema.NODE_KIND_FACT,
+    var declaredElement = node.declaredElement!;
+    _enclosingFilePath = _getPath(resourceProvider, declaredElement);
+    return _withEnclosingElement(declaredElement, () {
+      addFact(_enclosingFileVName!, schema.NODE_KIND_FACT,
           _encode(schema.FILE_KIND));
-      addFact(_enclosingFileVName, schema.TEXT_FACT, _encode(_contents));
-      addFact(_enclosingFileVName, schema.TEXT_ENCODING_FACT,
+      addFact(_enclosingFileVName!, schema.TEXT_FACT, _encode(_contents));
+      addFact(_enclosingFileVName!, schema.TEXT_ENCODING_FACT,
           _encode(schema.DEFAULT_TEXT_ENCODING));
 
       // handle LibraryDirective:
@@ -351,9 +349,9 @@
 
       // Don't use visitLibraryDirective as this won't generate a package
       // VName for libraries that don't have a library directive.
-      var libraryElement = node.declaredElement.library;
-      if (libraryElement.definingCompilationUnit == node.declaredElement) {
-        LibraryDirective libraryDirective;
+      var libraryElement = declaredElement.library;
+      if (libraryElement.definingCompilationUnit == declaredElement) {
+        LibraryDirective? libraryDirective;
         for (var directive in node.directives) {
           if (directive is LibraryDirective) {
             libraryDirective = directive;
@@ -389,18 +387,20 @@
 
   @override
   void visitConstructorDeclaration(ConstructorDeclaration node) {
-    return _withEnclosingElement(node.declaredElement, () {
+    var declaredElement = node.declaredElement!;
+    return _withEnclosingElement(declaredElement, () {
       // function/ constructor node
       var constructorVName = addNodeAndFacts(schema.FUNCTION_KIND,
-          element: node.declaredElement,
+          element: declaredElement,
           subKind: schema.CONSTRUCTOR_SUBKIND,
           completeFact: schema.DEFINITION);
 
       // anchor
       var start = node.returnType.offset;
       var end = node.returnType.end;
-      if (node.name != null) {
-        end = node.name.end;
+      var nameNode = node.name;
+      if (nameNode != null) {
+        end = nameNode.end;
       }
       addAnchorEdgesContainingEdge(
           start: start,
@@ -412,7 +412,7 @@
           enclosingTarget: _enclosingClassVName);
 
       // function type
-      addFunctionType(node.declaredElement, node.parameters, constructorVName,
+      addFunctionType(declaredElement, node.parameters, constructorVName,
           returnNode: node.returnType);
 
       // TODO(jwren) handle implicit constructor case
@@ -429,8 +429,9 @@
 
   @override
   void visitDeclaredIdentifier(DeclaredIdentifier node) {
-    _handleVariableDeclaration(node.declaredElement, node.identifier,
-        subKind: schema.LOCAL_SUBKIND, type: node.declaredElement.type);
+    var declaredElement = node.declaredElement!;
+    _handleVariableDeclaration(declaredElement, node.identifier,
+        subKind: schema.LOCAL_SUBKIND, type: declaredElement.type);
 
     // no children
   }
@@ -439,7 +440,7 @@
   void visitEnumConstantDeclaration(EnumConstantDeclaration node) {
     // constant node
     var constDeclVName =
-        addNodeAndFacts(schema.CONSTANT_KIND, element: node.declaredElement);
+        addNodeAndFacts(schema.CONSTANT_KIND, element: node.declaredElement!);
 
     // anchor- defines/binding, defines
     addAnchorEdgesContainingEdge(
@@ -456,7 +457,7 @@
 
   @override
   void visitEnumDeclaration(EnumDeclaration node) {
-    return _withEnclosingElement(node.declaredElement, () {
+    return _withEnclosingElement(node.declaredElement!, () {
       // record/ enum node
       addNodeAndFacts(schema.RECORD_KIND,
           nodeVName: _enclosingClassVName,
@@ -490,9 +491,9 @@
     // identifier
     // Specified as Element, not var, so that the type can be changed in the
     // if-block.
-    Element element = node.declaredElement;
+    Element? element = node.declaredElement;
     if (element is FieldFormalParameterElement) {
-      element = (element as FieldFormalParameterElement).field;
+      element = element.field;
     }
     _handleRefEdge(
       element,
@@ -510,10 +511,11 @@
 
   @override
   void visitFunctionDeclaration(FunctionDeclaration node) {
-    return _withEnclosingElement(node.declaredElement, () {
+    var declaredElement = node.declaredElement!;
+    return _withEnclosingElement(declaredElement, () {
       // function node
       var functionVName = addNodeAndFacts(schema.FUNCTION_KIND,
-          element: node.declaredElement, completeFact: schema.DEFINITION);
+          element: declaredElement, completeFact: schema.DEFINITION);
 
       // anchor- defines/binding
       addAnchorEdgesContainingEdge(
@@ -533,8 +535,8 @@
           target: functionVName);
 
       // function type
-      addFunctionType(node.declaredElement, node.functionExpression.parameters,
-          functionVName,
+      addFunctionType(
+          declaredElement, node.functionExpression.parameters, functionVName,
           returnNode: node.returnType);
 
       _safelyVisit(node.documentationComment);
@@ -547,7 +549,7 @@
   @override
   void visitFunctionExpression(FunctionExpression node) {
     return _withEnclosingElement(
-        node.declaredElement, () => super.visitFunctionExpression(node));
+        node.declaredElement!, () => super.visitFunctionExpression(node));
   }
 
   @override
@@ -558,7 +560,7 @@
     var returnType = node.returnType;
     if (returnType is TypeName) {
       _handleRefEdge(
-        returnType.name?.staticElement,
+        returnType.name.staticElement,
         const <String>[schema.REF_EDGE],
         syntacticEntity: returnType.name,
       );
@@ -663,10 +665,11 @@
 
   @override
   void visitMethodDeclaration(MethodDeclaration node) {
-    return _withEnclosingElement(node.declaredElement, () {
+    var declaredElement = node.declaredElement!;
+    return _withEnclosingElement(declaredElement, () {
       // function node
       var methodVName = addNodeAndFacts(schema.FUNCTION_KIND,
-          element: node.declaredElement, completeFact: schema.DEFINITION);
+          element: declaredElement, completeFact: schema.DEFINITION);
 
       // anchor- defines/binding
       addAnchorEdgesContainingEdge(
@@ -686,23 +689,25 @@
           target: methodVName);
 
       // function type
-      addFunctionType(node.declaredElement, node.parameters, methodVName,
+      addFunctionType(declaredElement, node.parameters, methodVName,
           returnNode: node.returnType);
 
       // override edges
       var overriddenList = _inheritanceManager.getOverridden2(
-        _enclosingClassElement,
+        _enclosingClassElement!,
         Name(
-          _enclosingClassElement.library.source.uri,
-          node.declaredElement.name,
+          _enclosingClassElement!.library.source.uri,
+          declaredElement.name,
         ),
       );
-      for (var overridden in overriddenList) {
-        addEdge(
-          methodVName,
-          schema.OVERRIDES_EDGE,
-          _vNameFromElement(overridden, schema.FUNCTION_KIND),
-        );
+      if (overriddenList != null) {
+        for (var overridden in overriddenList) {
+          addEdge(
+            methodVName,
+            schema.OVERRIDES_EDGE,
+            _vNameFromElement(overridden, schema.FUNCTION_KIND),
+          );
+        }
       }
 
       // visit children
@@ -717,7 +722,7 @@
 
   @override
   void visitMethodInvocation(MethodInvocation node) {
-    var element = node.methodName?.staticElement;
+    var element = node.methodName.staticElement;
 
     // anchor- ref/call
     _handleRefCallEdge(element, syntacticEntity: node.methodName);
@@ -731,15 +736,17 @@
   @override
   void visitSimpleFormalParameter(SimpleFormalParameter node) {
     // parameter node
+    var declaredElement = node.declaredElement!;
     var paramVName = addNodeAndFacts(schema.VARIABLE_KIND,
-        element: node.declaredElement,
+        element: declaredElement,
         subKind: schema.LOCAL_PARAMETER_SUBKIND,
         completeFact: schema.DEFINITION);
 
     // node.identifier can be null in cases with the new generic function type
     // syntax
     // TODO(jwren) add test cases for this situation
-    if (node.identifier != null) {
+    var identifier = node.identifier;
+    if (identifier != null) {
       // The anchor and anchor edges generation are broken into two cases, the
       // first case is "method(parameter_name) ...", where the the parameter
       // character range only includes a parameter name.  The second case is for
@@ -749,11 +756,11 @@
       // With the first case a single anchor range is created, for the second
       // case an anchor is created on parameter_name, as well as the range
       // including any prefixes.
-      if (node.offset == node.identifier.offset &&
-          node.length == node.identifier.length) {
+      if (node.offset == identifier.offset &&
+          node.length == identifier.length) {
         // anchor- defines/binding, defines
         addAnchorEdgesContainingEdge(
-            syntacticEntity: node.identifier,
+            syntacticEntity: identifier,
             edges: [
               schema.DEFINES_BINDING_EDGE,
               schema.DEFINES_EDGE,
@@ -763,7 +770,7 @@
       } else {
         // anchor- defines/binding
         addAnchorEdgesContainingEdge(
-            syntacticEntity: node.identifier,
+            syntacticEntity: identifier,
             edges: [
               schema.DEFINES_BINDING_EDGE,
             ],
@@ -781,8 +788,8 @@
     }
 
     // type
-    addEdge(paramVName, schema.TYPED_EDGE,
-        _vNameFromType(node.declaredElement.type));
+    addEdge(
+        paramVName, schema.TYPED_EDGE, _vNameFromType(declaredElement.type));
 
     // visit children
     _safelyVisit(node.documentationComment);
@@ -841,33 +848,38 @@
         _enclosingVName != _enclosingFileVName;
 
     // variable
-    _handleVariableDeclaration(node.declaredElement, node.name,
+    var declaredElement = node.declaredElement!;
+    _handleVariableDeclaration(declaredElement, node.name,
         subKind: isLocal ? schema.LOCAL_SUBKIND : schema.FIELD_SUBKIND,
-        type: node.declaredElement.type);
+        type: declaredElement.type);
 
     // visit children
     _safelyVisit(node.initializer);
   }
 
-  Element _findNonSyntheticElement(Element element) {
+  Element? _findNonSyntheticElement(Element? element) {
     if (element == null || !element.isSynthetic) {
       return element;
     }
     if (element is PropertyAccessorElement) {
       if (!element.variable.isSynthetic) {
         return element.variable;
-      } else if (element.correspondingGetter != null &&
-          !element.correspondingGetter.isSynthetic) {
-        return element.correspondingGetter;
-      } else if (element.correspondingSetter != null &&
-          !element.correspondingSetter.isSynthetic) {
-        return element.correspondingSetter;
+      }
+
+      var correspondingGetter = element.correspondingGetter;
+      if (correspondingGetter != null && !correspondingGetter.isSynthetic) {
+        return correspondingGetter;
+      }
+
+      var correspondingSetter = element.correspondingSetter;
+      if (correspondingSetter != null && !correspondingSetter.isSynthetic) {
+        return correspondingSetter;
       }
     }
     return null;
   }
 
-  String _getNodeKind(Element e) {
+  String? _getNodeKind(Element e) {
     if (e is FieldElement && e.isEnumConstant) {
       // FieldElement is a kind of VariableElement, so this test case must be
       // before the e is VariableElement check.
@@ -885,11 +897,11 @@
   }
 
   void _handleRefCallEdge(
-    Element element, {
-    SyntacticEntity syntacticEntity,
+    Element? element, {
+    SyntacticEntity? syntacticEntity,
     int start = _notFound,
     int end = _notFound,
-    KytheVName enclosingTarget,
+    KytheVName? enclosingTarget,
   }) {
     if (element is ExecutableElement &&
         _enclosingVName != _enclosingFileVName) {
@@ -920,14 +932,14 @@
   /// be non-empty, and are added from the anchor to the target generated using
   /// the passed [Element]. The created [KytheVName] is returned, if not `null`
   /// is returned.
-  KytheVName _handleRefEdge(
-    Element element,
+  KytheVName? _handleRefEdge(
+    Element? element,
     List<String> refEdgeTypes, {
-    SyntacticEntity syntacticEntity,
+    SyntacticEntity? syntacticEntity,
     int start = _notFound,
     int end = _notFound,
-    KytheVName enclosingTarget,
-    KytheVName enclosingAnchor,
+    KytheVName? enclosingTarget,
+    KytheVName? enclosingAnchor,
   }) {
     assert(refEdgeTypes.isNotEmpty);
     element = _findNonSyntheticElement(element);
@@ -941,7 +953,6 @@
       return null;
     }
     var vName = _vNameFromElement(element, nodeKind);
-    assert(vName != null);
 
     // anchor
     addAnchorEdgesContainingEdge(
@@ -964,7 +975,7 @@
       // supertype, but it returns the type of the enclosing class (same as
       // ThisExpression), do some additional work to correct assumption:
       if (thisOrSuperNode is SuperExpression && type.element is ClassElement) {
-        DartType supertype = (type.element as ClassElement).supertype;
+        var supertype = (type.element as ClassElement).supertype;
         if (supertype != null) {
           type = supertype;
         }
@@ -979,7 +990,7 @@
           target: vName);
 
       // childof from the anchor
-      addEdge(anchorVName, schema.CHILD_OF_EDGE, _enclosingVName);
+      addEdge(anchorVName, schema.CHILD_OF_EDGE, _enclosingVName!);
     }
 
     // no children to visit
@@ -988,7 +999,7 @@
   /// Add a "ref/imports" edge from the passed [uriNode] location to the
   /// [referencedElement] [Element].  If the passed element is null, the edge is
   /// not written out.
-  void _handleUriReference(StringLiteral uriNode, Element referencedElement) {
+  void _handleUriReference(StringLiteral uriNode, Element? referencedElement) {
     if (referencedElement != null) {
       var start = uriNode.offset;
       var end = uriNode.end;
@@ -1016,8 +1027,8 @@
   }
 
   void _handleVariableDeclaration(
-      Element element, SyntacticEntity syntacticEntity,
-      {String subKind, DartType type}) {
+      Element? element, SyntacticEntity syntacticEntity,
+      {String? subKind, DartType? type}) {
     // variable
     var variableVName = addNodeAndFacts(schema.VARIABLE_KIND,
         element: element, subKind: subKind, completeFact: schema.DEFINITION);
@@ -1039,7 +1050,6 @@
 
   void _handleVariableDeclarationListAnnotations(
       VariableDeclarationList variableDeclarationList, KytheVName refVName) {
-    assert(refVName != null);
     for (var varDecl in variableDeclarationList.variables) {
       if (varDecl.declaredElement != null) {
         var parentVName =
@@ -1053,14 +1063,14 @@
   }
 
   /// If the given [node] is not `null`, accept this visitor.
-  void _safelyVisit(AstNode node) {
+  void _safelyVisit(AstNode? node) {
     if (node != null) {
       node.accept(this);
     }
   }
 
   /// If the given [nodeList] is not `null`, accept this visitor.
-  void _safelyVisitList(NodeList nodeList) {
+  void _safelyVisitList(NodeList? nodeList) {
     if (nodeList != null) {
       nodeList.accept(this);
     }
@@ -1068,7 +1078,7 @@
 
   void _withEnclosingElement(Element element, Function() f) {
     var outerEnclosingElement = _enclosingElement;
-    Element outerEnclosingClassElement = _enclosingClassElement;
+    var outerEnclosingClassElement = _enclosingClassElement;
     var outerEnclosingVName = _enclosingVName;
     var outerEnclosingClassVName = _enclosingClassVName;
     try {
@@ -1132,13 +1142,13 @@
   /// Finally, for all anchors, a childof edge with a target of the enclosing
   /// file is written out.
   KytheVName addAnchorEdgesContainingEdge({
-    SyntacticEntity syntacticEntity,
+    SyntacticEntity? syntacticEntity,
     int start = _notFound,
     int end = _notFound,
     List<String> edges = const [],
-    KytheVName target,
-    KytheVName enclosingTarget,
-    KytheVName enclosingAnchor,
+    KytheVName? target,
+    KytheVName? enclosingTarget,
+    KytheVName? enclosingAnchor,
   }) {
     if (start == _notFound && end == _notFound) {
       if (syntacticEntity != null) {
@@ -1193,11 +1203,8 @@
     }
   }
 
-  KytheEntry addEntry(KytheVName source, String edgeKind, KytheVName target,
+  KytheEntry addEntry(KytheVName source, String? edgeKind, KytheVName? target,
       String factName, List<int> factValue) {
-    assert(source != null);
-    assert(factName != null);
-    assert(factValue != null);
     // factValue may be an empty array, the fact may be that a file text or
     // document text is empty
     if (edgeKind == null || edgeKind.isEmpty) {
@@ -1218,20 +1225,20 @@
   /// This is a convenience method for adding function types.
   KytheVName addFunctionType(
     Element functionElement,
-    FormalParameterList paramNodes,
+    FormalParameterList? paramNodes,
     KytheVName functionVName, {
-    AstNode returnNode,
+    AstNode? returnNode,
   }) {
     var i = 0;
     var funcTypeVName =
         addNodeAndFacts(schema.TAPP_KIND, element: functionElement);
     addEdge(funcTypeVName, schema.PARAM_EDGE, fnBuiltin, ordinalIntValue: i++);
 
-    KytheVName returnTypeVName;
+    KytheVName? returnTypeVName;
     if (returnNode is TypeName) {
       // MethodDeclaration and FunctionDeclaration both return a TypeName from
       // returnType
-      if (returnNode.type.isVoid) {
+      if (returnNode.typeOrThrow.isVoid) {
         returnTypeVName = voidBuiltin;
       } else {
         returnTypeVName =
@@ -1239,7 +1246,7 @@
       }
     } else if (returnNode is Identifier) {
       // ConstructorDeclaration returns an Identifier from returnType
-      if (returnNode.staticType.isVoid) {
+      if (returnNode.typeOrThrow.isVoid) {
         returnTypeVName = voidBuiltin;
       } else {
         returnTypeVName =
@@ -1256,9 +1263,10 @@
     if (paramNodes != null) {
       for (var paramNode in paramNodes.parameters) {
         var paramTypeVName = dynamicBuiltin;
-        if (!paramNode.declaredElement.type.isDynamic) {
-          paramTypeVName = _vNameFromElement(
-              paramNode.declaredElement.type.element, schema.TAPP_KIND);
+        var declaredElement = paramNode.declaredElement!;
+        var type = declaredElement.type;
+        if (!type.isDynamic) {
+          paramTypeVName = _vNameFromElement(type.element, schema.TAPP_KIND);
         }
         addEdge(funcTypeVName, schema.PARAM_EDGE, paramTypeVName,
             ordinalIntValue: i++);
@@ -1276,10 +1284,10 @@
   /// currently guarantee that the inputs to these fact kinds are valid for the
   /// associated nodeKind- if a non-null, then it will set.
   KytheVName addNodeAndFacts(String nodeKind,
-      {Element element,
-      KytheVName nodeVName,
-      String subKind,
-      String completeFact}) {
+      {Element? element,
+      KytheVName? nodeVName,
+      String? subKind,
+      String? completeFact}) {
     nodeVName ??= _vNameFromElement(element, nodeKind);
     addFact(nodeVName, schema.NODE_KIND_FACT, _encode(nodeKind));
     if (subKind != null) {
@@ -1321,7 +1329,7 @@
 
   /// Given some [Element] and Kythe node kind, this method generates and
   /// returns the [KytheVName].
-  KytheVName _vNameFromElement(Element e, String nodeKind) {
+  KytheVName _vNameFromElement(Element? e, String nodeKind) {
     assert(nodeKind != schema.FILE_KIND);
     // general case
     return _vName(_getSignature(resourceProvider, e, nodeKind, corpus), corpus,
@@ -1329,7 +1337,7 @@
   }
 
   /// Returns a [KytheVName] corresponding to the given [DartType].
-  KytheVName _vNameFromType(DartType type) {
+  KytheVName _vNameFromType(DartType? type) {
     if (type == null || type.isDynamic) {
       return dynamicBuiltin;
     } else if (type.isVoid) {
@@ -1358,8 +1366,8 @@
   @override
   StringBuffer visitElement(Element e) {
     assert(e is! MultiplyInheritedExecutableElement);
-    var enclosingElt = e.enclosingElement;
-    var buffer = enclosingElt.accept(this);
+    var enclosingElt = e.enclosingElement!;
+    var buffer = enclosingElt.accept(this)!;
     if (buffer.isNotEmpty) {
       buffer.write('#');
     }
@@ -1386,6 +1394,6 @@
     // It is legal to have a named constructor with the same name as a type
     // parameter.  So we distinguish them by using '.' between the class (or
     // typedef) name and the type parameter name.
-    return e.enclosingElement.accept(this)..write('.')..write(e.name);
+    return e.enclosingElement!.accept(this)!..write('.')..write(e.name);
   }
 }
diff --git a/pkg/front_end/lib/src/fasta/kernel/kernel_target.dart b/pkg/front_end/lib/src/fasta/kernel/kernel_target.dart
index 456dc09..66d862b 100644
--- a/pkg/front_end/lib/src/fasta/kernel/kernel_target.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/kernel_target.dart
@@ -1266,7 +1266,7 @@
     ticker.logMs("Evaluated constants");
 
     backendTarget.performTransformationsOnProcedure(
-        loader.coreTypes, loader.hierarchy, procedure,
+        loader.coreTypes, loader.hierarchy, procedure, environmentDefines,
         logger: (String msg) => ticker.logMs(msg));
   }
 
diff --git a/pkg/front_end/testcases/general/error_recovery/issue_39033.crash_dart.weak.expect b/pkg/front_end/testcases/general/error_recovery/issue_39033.crash_dart.weak.expect
index 40ebcfd..3e48c20 100644
--- a/pkg/front_end/testcases/general/error_recovery/issue_39033.crash_dart.weak.expect
+++ b/pkg/front_end/testcases/general/error_recovery/issue_39033.crash_dart.weak.expect
@@ -14,7 +14,7 @@
 // typedef F<Glib.=
 //                 ^...
 //
-// pkg/front_end/testcases/general/error_recovery/issue_39033.crash_dart:1:16: Error: Can't create typedef from non-function type.
+// pkg/front_end/testcases/general/error_recovery/issue_39033.crash_dart:1:16: Error: Can't create typedef from non-type.
 // typedef F<Glib.=
 //                ^
 //
diff --git a/pkg/front_end/testcases/general/error_recovery/issue_39033.crash_dart.weak.outline.expect b/pkg/front_end/testcases/general/error_recovery/issue_39033.crash_dart.weak.outline.expect
index 40ebcfd..3e48c20 100644
--- a/pkg/front_end/testcases/general/error_recovery/issue_39033.crash_dart.weak.outline.expect
+++ b/pkg/front_end/testcases/general/error_recovery/issue_39033.crash_dart.weak.outline.expect
@@ -14,7 +14,7 @@
 // typedef F<Glib.=
 //                 ^...
 //
-// pkg/front_end/testcases/general/error_recovery/issue_39033.crash_dart:1:16: Error: Can't create typedef from non-function type.
+// pkg/front_end/testcases/general/error_recovery/issue_39033.crash_dart:1:16: Error: Can't create typedef from non-type.
 // typedef F<Glib.=
 //                ^
 //
diff --git a/pkg/front_end/testcases/general/error_recovery/issue_39033.crash_dart.weak.transformed.expect b/pkg/front_end/testcases/general/error_recovery/issue_39033.crash_dart.weak.transformed.expect
index 40ebcfd..3e48c20 100644
--- a/pkg/front_end/testcases/general/error_recovery/issue_39033.crash_dart.weak.transformed.expect
+++ b/pkg/front_end/testcases/general/error_recovery/issue_39033.crash_dart.weak.transformed.expect
@@ -14,7 +14,7 @@
 // typedef F<Glib.=
 //                 ^...
 //
-// pkg/front_end/testcases/general/error_recovery/issue_39033.crash_dart:1:16: Error: Can't create typedef from non-function type.
+// pkg/front_end/testcases/general/error_recovery/issue_39033.crash_dart:1:16: Error: Can't create typedef from non-type.
 // typedef F<Glib.=
 //                ^
 //
diff --git a/pkg/front_end/testcases/general/error_recovery/issue_39033b.crash_dart b/pkg/front_end/testcases/general/error_recovery/issue_39033b.crash_dart
new file mode 100644
index 0000000..847dafa
--- /dev/null
+++ b/pkg/front_end/testcases/general/error_recovery/issue_39033b.crash_dart
@@ -0,0 +1,2 @@
+// @dart=2.12
+typedef F<Glib.=
\ No newline at end of file
diff --git a/pkg/front_end/testcases/general/error_recovery/issue_39033b.crash_dart.textual_outline.expect b/pkg/front_end/testcases/general/error_recovery/issue_39033b.crash_dart.textual_outline.expect
new file mode 100644
index 0000000..5c0f5bd
--- /dev/null
+++ b/pkg/front_end/testcases/general/error_recovery/issue_39033b.crash_dart.textual_outline.expect
@@ -0,0 +1,2 @@
+// @dart = 2.12
+typedef F<Glib>.=;
diff --git a/pkg/front_end/testcases/general/error_recovery/issue_39033b.crash_dart.weak.expect b/pkg/front_end/testcases/general/error_recovery/issue_39033b.crash_dart.weak.expect
new file mode 100644
index 0000000..6d27355
--- /dev/null
+++ b/pkg/front_end/testcases/general/error_recovery/issue_39033b.crash_dart.weak.expect
@@ -0,0 +1,24 @@
+library /*isNonNullableByDefault*/;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/general/error_recovery/issue_39033b.crash_dart:2:11: Error: Expected '>' after this.
+// typedef F<Glib.=
+//           ^^^^
+//
+// pkg/front_end/testcases/general/error_recovery/issue_39033b.crash_dart:2:17: Error: Expected a type, but got ''.
+// typedef F<Glib.=
+//                 ^...
+//
+// pkg/front_end/testcases/general/error_recovery/issue_39033b.crash_dart:2:17: Error: Expected ';' after this.
+// typedef F<Glib.=
+//                 ^...
+//
+// pkg/front_end/testcases/general/error_recovery/issue_39033b.crash_dart:2:16: Error: Can't create typedef from non-function type.
+// typedef F<Glib.=
+//                ^
+//
+import self as self;
+import "dart:core" as core;
+
+typedef F<unrelated Glib extends core::Object? = dynamic> = invalid-type;
diff --git a/pkg/front_end/testcases/general/error_recovery/issue_39033b.crash_dart.weak.outline.expect b/pkg/front_end/testcases/general/error_recovery/issue_39033b.crash_dart.weak.outline.expect
new file mode 100644
index 0000000..6d27355
--- /dev/null
+++ b/pkg/front_end/testcases/general/error_recovery/issue_39033b.crash_dart.weak.outline.expect
@@ -0,0 +1,24 @@
+library /*isNonNullableByDefault*/;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/general/error_recovery/issue_39033b.crash_dart:2:11: Error: Expected '>' after this.
+// typedef F<Glib.=
+//           ^^^^
+//
+// pkg/front_end/testcases/general/error_recovery/issue_39033b.crash_dart:2:17: Error: Expected a type, but got ''.
+// typedef F<Glib.=
+//                 ^...
+//
+// pkg/front_end/testcases/general/error_recovery/issue_39033b.crash_dart:2:17: Error: Expected ';' after this.
+// typedef F<Glib.=
+//                 ^...
+//
+// pkg/front_end/testcases/general/error_recovery/issue_39033b.crash_dart:2:16: Error: Can't create typedef from non-function type.
+// typedef F<Glib.=
+//                ^
+//
+import self as self;
+import "dart:core" as core;
+
+typedef F<unrelated Glib extends core::Object? = dynamic> = invalid-type;
diff --git a/pkg/front_end/testcases/general/error_recovery/issue_39033b.crash_dart.weak.transformed.expect b/pkg/front_end/testcases/general/error_recovery/issue_39033b.crash_dart.weak.transformed.expect
new file mode 100644
index 0000000..6d27355
--- /dev/null
+++ b/pkg/front_end/testcases/general/error_recovery/issue_39033b.crash_dart.weak.transformed.expect
@@ -0,0 +1,24 @@
+library /*isNonNullableByDefault*/;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/general/error_recovery/issue_39033b.crash_dart:2:11: Error: Expected '>' after this.
+// typedef F<Glib.=
+//           ^^^^
+//
+// pkg/front_end/testcases/general/error_recovery/issue_39033b.crash_dart:2:17: Error: Expected a type, but got ''.
+// typedef F<Glib.=
+//                 ^...
+//
+// pkg/front_end/testcases/general/error_recovery/issue_39033b.crash_dart:2:17: Error: Expected ';' after this.
+// typedef F<Glib.=
+//                 ^...
+//
+// pkg/front_end/testcases/general/error_recovery/issue_39033b.crash_dart:2:16: Error: Can't create typedef from non-function type.
+// typedef F<Glib.=
+//                ^
+//
+import self as self;
+import "dart:core" as core;
+
+typedef F<unrelated Glib extends core::Object? = dynamic> = invalid-type;
diff --git a/pkg/front_end/testcases/nnbd/nullable_rhs_of_typedef.dart b/pkg/front_end/testcases/nnbd/nullable_rhs_of_typedef.dart
index 3d43a76..3535a5c 100644
--- a/pkg/front_end/testcases/nnbd/nullable_rhs_of_typedef.dart
+++ b/pkg/front_end/testcases/nnbd/nullable_rhs_of_typedef.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
+
 typedef F = void Function()?;
 
 void foo(void Function() x) {
diff --git a/pkg/front_end/testcases/nnbd/nullable_rhs_of_typedef.dart.strong.expect b/pkg/front_end/testcases/nnbd/nullable_rhs_of_typedef.dart.strong.expect
index dd56766..5e0924e 100644
--- a/pkg/front_end/testcases/nnbd/nullable_rhs_of_typedef.dart.strong.expect
+++ b/pkg/front_end/testcases/nnbd/nullable_rhs_of_typedef.dart.strong.expect
@@ -2,7 +2,7 @@
 //
 // Problems in library:
 //
-// pkg/front_end/testcases/nnbd/nullable_rhs_of_typedef.dart:5:11: Error: Can't create typedef from nullable type.
+// pkg/front_end/testcases/nnbd/nullable_rhs_of_typedef.dart:7:11: Error: Can't create typedef from nullable type.
 // typedef F = void Function()?;
 //           ^
 //
diff --git a/pkg/front_end/testcases/nnbd/nullable_rhs_of_typedef.dart.strong.transformed.expect b/pkg/front_end/testcases/nnbd/nullable_rhs_of_typedef.dart.strong.transformed.expect
index dd56766..5e0924e 100644
--- a/pkg/front_end/testcases/nnbd/nullable_rhs_of_typedef.dart.strong.transformed.expect
+++ b/pkg/front_end/testcases/nnbd/nullable_rhs_of_typedef.dart.strong.transformed.expect
@@ -2,7 +2,7 @@
 //
 // Problems in library:
 //
-// pkg/front_end/testcases/nnbd/nullable_rhs_of_typedef.dart:5:11: Error: Can't create typedef from nullable type.
+// pkg/front_end/testcases/nnbd/nullable_rhs_of_typedef.dart:7:11: Error: Can't create typedef from nullable type.
 // typedef F = void Function()?;
 //           ^
 //
diff --git a/pkg/front_end/testcases/nnbd/nullable_rhs_of_typedef.dart.textual_outline.expect b/pkg/front_end/testcases/nnbd/nullable_rhs_of_typedef.dart.textual_outline.expect
index a732d24..cd16a79 100644
--- a/pkg/front_end/testcases/nnbd/nullable_rhs_of_typedef.dart.textual_outline.expect
+++ b/pkg/front_end/testcases/nnbd/nullable_rhs_of_typedef.dart.textual_outline.expect
@@ -1,3 +1,4 @@
+// @dart = 2.12
 typedef F = void Function()?;
 void foo(void Function() x) {}
 void bar(F x) {}
diff --git a/pkg/front_end/testcases/nnbd/nullable_rhs_of_typedef.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/nnbd/nullable_rhs_of_typedef.dart.textual_outline_modelled.expect
index 091d52a..77912b2 100644
--- a/pkg/front_end/testcases/nnbd/nullable_rhs_of_typedef.dart.textual_outline_modelled.expect
+++ b/pkg/front_end/testcases/nnbd/nullable_rhs_of_typedef.dart.textual_outline_modelled.expect
@@ -1,3 +1,4 @@
+// @dart = 2.12
 main() {}
 typedef F = void Function()?;
 void bar(F x) {}
diff --git a/pkg/front_end/testcases/nnbd/nullable_rhs_of_typedef.dart.weak.expect b/pkg/front_end/testcases/nnbd/nullable_rhs_of_typedef.dart.weak.expect
index dd56766..5e0924e 100644
--- a/pkg/front_end/testcases/nnbd/nullable_rhs_of_typedef.dart.weak.expect
+++ b/pkg/front_end/testcases/nnbd/nullable_rhs_of_typedef.dart.weak.expect
@@ -2,7 +2,7 @@
 //
 // Problems in library:
 //
-// pkg/front_end/testcases/nnbd/nullable_rhs_of_typedef.dart:5:11: Error: Can't create typedef from nullable type.
+// pkg/front_end/testcases/nnbd/nullable_rhs_of_typedef.dart:7:11: Error: Can't create typedef from nullable type.
 // typedef F = void Function()?;
 //           ^
 //
diff --git a/pkg/front_end/testcases/nnbd/nullable_rhs_of_typedef.dart.weak.outline.expect b/pkg/front_end/testcases/nnbd/nullable_rhs_of_typedef.dart.weak.outline.expect
index 4c7e4f3..3741099 100644
--- a/pkg/front_end/testcases/nnbd/nullable_rhs_of_typedef.dart.weak.outline.expect
+++ b/pkg/front_end/testcases/nnbd/nullable_rhs_of_typedef.dart.weak.outline.expect
@@ -2,7 +2,7 @@
 //
 // Problems in library:
 //
-// pkg/front_end/testcases/nnbd/nullable_rhs_of_typedef.dart:5:11: Error: Can't create typedef from nullable type.
+// pkg/front_end/testcases/nnbd/nullable_rhs_of_typedef.dart:7:11: Error: Can't create typedef from nullable type.
 // typedef F = void Function()?;
 //           ^
 //
diff --git a/pkg/front_end/testcases/nnbd/nullable_rhs_of_typedef.dart.weak.transformed.expect b/pkg/front_end/testcases/nnbd/nullable_rhs_of_typedef.dart.weak.transformed.expect
index dd56766..5e0924e 100644
--- a/pkg/front_end/testcases/nnbd/nullable_rhs_of_typedef.dart.weak.transformed.expect
+++ b/pkg/front_end/testcases/nnbd/nullable_rhs_of_typedef.dart.weak.transformed.expect
@@ -2,7 +2,7 @@
 //
 // Problems in library:
 //
-// pkg/front_end/testcases/nnbd/nullable_rhs_of_typedef.dart:5:11: Error: Can't create typedef from nullable type.
+// pkg/front_end/testcases/nnbd/nullable_rhs_of_typedef.dart:7:11: Error: Can't create typedef from nullable type.
 // typedef F = void Function()?;
 //           ^
 //
diff --git a/pkg/front_end/testcases/nnbd_mixed/opt_out.dart b/pkg/front_end/testcases/nnbd_mixed/opt_out.dart
index 48e08b3..5e5ca5a 100644
--- a/pkg/front_end/testcases/nnbd_mixed/opt_out.dart
+++ b/pkg/front_end/testcases/nnbd_mixed/opt_out.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
+
 // This file contains benign use of NNBD features that shouldn't result in
 // compile-time errors.
 
diff --git a/pkg/front_end/testcases/nnbd_mixed/opt_out.dart.textual_outline.expect b/pkg/front_end/testcases/nnbd_mixed/opt_out.dart.textual_outline.expect
index be1c56c..86d05f9 100644
--- a/pkg/front_end/testcases/nnbd_mixed/opt_out.dart.textual_outline.expect
+++ b/pkg/front_end/testcases/nnbd_mixed/opt_out.dart.textual_outline.expect
@@ -1,3 +1,4 @@
+// @dart = 2.12
 import 'opt_out_lib.dart';
 
 class A<T> {
diff --git a/pkg/front_end/testcases/nnbd_mixed/opt_out.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/nnbd_mixed/opt_out.dart.textual_outline_modelled.expect
index a3c6fff..83adde2 100644
--- a/pkg/front_end/testcases/nnbd_mixed/opt_out.dart.textual_outline_modelled.expect
+++ b/pkg/front_end/testcases/nnbd_mixed/opt_out.dart.textual_outline_modelled.expect
@@ -1,3 +1,4 @@
+// @dart = 2.12
 import 'opt_out_lib.dart';
 List<String?> l = [];
 String? s = null;
diff --git a/pkg/front_end/testcases/nnbd_mixed/opt_out.dart.weak.expect b/pkg/front_end/testcases/nnbd_mixed/opt_out.dart.weak.expect
index df7a1ae..3e54fc2 100644
--- a/pkg/front_end/testcases/nnbd_mixed/opt_out.dart.weak.expect
+++ b/pkg/front_end/testcases/nnbd_mixed/opt_out.dart.weak.expect
@@ -2,7 +2,7 @@
 //
 // Problems in library:
 //
-// pkg/front_end/testcases/nnbd_mixed/opt_out.dart:16:11: Error: Can't create typedef from nullable type.
+// pkg/front_end/testcases/nnbd_mixed/opt_out.dart:18:11: Error: Can't create typedef from nullable type.
 // typedef F = void Function()?;
 //           ^
 //
diff --git a/pkg/front_end/testcases/nnbd_mixed/opt_out.dart.weak.outline.expect b/pkg/front_end/testcases/nnbd_mixed/opt_out.dart.weak.outline.expect
index c67bc6d..de1edfa 100644
--- a/pkg/front_end/testcases/nnbd_mixed/opt_out.dart.weak.outline.expect
+++ b/pkg/front_end/testcases/nnbd_mixed/opt_out.dart.weak.outline.expect
@@ -2,7 +2,7 @@
 //
 // Problems in library:
 //
-// pkg/front_end/testcases/nnbd_mixed/opt_out.dart:16:11: Error: Can't create typedef from nullable type.
+// pkg/front_end/testcases/nnbd_mixed/opt_out.dart:18:11: Error: Can't create typedef from nullable type.
 // typedef F = void Function()?;
 //           ^
 //
diff --git a/pkg/front_end/testcases/nnbd_mixed/opt_out.dart.weak.transformed.expect b/pkg/front_end/testcases/nnbd_mixed/opt_out.dart.weak.transformed.expect
index 602aa76..3924be2 100644
--- a/pkg/front_end/testcases/nnbd_mixed/opt_out.dart.weak.transformed.expect
+++ b/pkg/front_end/testcases/nnbd_mixed/opt_out.dart.weak.transformed.expect
@@ -2,7 +2,7 @@
 //
 // Problems in library:
 //
-// pkg/front_end/testcases/nnbd_mixed/opt_out.dart:16:11: Error: Can't create typedef from nullable type.
+// pkg/front_end/testcases/nnbd_mixed/opt_out.dart:18:11: Error: Can't create typedef from nullable type.
 // typedef F = void Function()?;
 //           ^
 //
diff --git a/pkg/front_end/testcases/text_serialization.status b/pkg/front_end/testcases/text_serialization.status
index 55f591f..6b0b79c 100644
--- a/pkg/front_end/testcases/text_serialization.status
+++ b/pkg/front_end/testcases/text_serialization.status
@@ -55,6 +55,7 @@
 general/error_recovery/issue_39026.crash: RuntimeError
 general/error_recovery/issue_39026_prime.crash: RuntimeError
 general/error_recovery/issue_39033.crash: RuntimeError
+general/error_recovery/issue_39033b.crash: RuntimeError
 general/error_recovery/issue_39058.crash: RuntimeError
 general/error_recovery/issue_39058_prime.crash: RuntimeError
 general/error_recovery/issue_39202.crash: RuntimeError
diff --git a/pkg/front_end/testcases/textual_outline.status b/pkg/front_end/testcases/textual_outline.status
index 6dd5ffb..7a46a53 100644
--- a/pkg/front_end/testcases/textual_outline.status
+++ b/pkg/front_end/testcases/textual_outline.status
@@ -20,6 +20,8 @@
 regress/issue_39091_2: EmptyOutput
 regress/utf_16_le_content.crash: EmptyOutput
 
+dart2js/late_fields: FormatterCrash
+dart2js/late_statics: FormatterCrash
 const_functions/const_functions_const_factory: FormatterCrash
 extensions/extension_constructor: FormatterCrash
 extensions/extension_field_with_type_parameter_usage: FormatterCrash
@@ -48,6 +50,7 @@
 general/error_recovery/constructor_recovery_set: FormatterCrash
 general/error_recovery/issue_22313: FormatterCrash
 general/error_recovery/issue_39033.crash: FormatterCrash
+general/error_recovery/issue_39033b.crash: FormatterCrash
 general/error_recovery/issue_39058_prime.crash: FormatterCrash
 general/error_recovery/issue_39202.crash: FormatterCrash
 general/error_recovery/issue_39230.crash: FormatterCrash
diff --git a/pkg/front_end/testcases/weak.status b/pkg/front_end/testcases/weak.status
index d4a488a..9af0078 100644
--- a/pkg/front_end/testcases/weak.status
+++ b/pkg/front_end/testcases/weak.status
@@ -59,6 +59,7 @@
 general/error_recovery/issue_39026.crash: RuntimeError
 general/error_recovery/issue_39026_prime.crash: RuntimeError
 general/error_recovery/issue_39033.crash: RuntimeError
+general/error_recovery/issue_39033b.crash: RuntimeError
 general/error_recovery/issue_39058.crash: RuntimeError
 general/error_recovery/issue_39058_prime.crash: RuntimeError
 general/error_recovery/issue_39202.crash: RuntimeError
diff --git a/pkg/kernel/lib/core_types.dart b/pkg/kernel/lib/core_types.dart
index 337ed54..98160cf 100644
--- a/pkg/kernel/lib/core_types.dart
+++ b/pkg/kernel/lib/core_types.dart
@@ -161,11 +161,11 @@
   late final Constructor futureImplConstructor =
       index.getMember('dart:async', '_Future', '') as Constructor;
 
-  late final Member completeOnAsyncReturn =
-      index.getTopLevelMember('dart:async', '_completeOnAsyncReturn');
+  late final Procedure completeOnAsyncReturn = index.getTopLevelMember(
+      'dart:async', '_completeOnAsyncReturn') as Procedure;
 
-  late final Member completeOnAsyncError =
-      index.getTopLevelMember('dart:async', '_completeOnAsyncError');
+  late final Procedure completeOnAsyncError = index.getTopLevelMember(
+      'dart:async', '_completeOnAsyncError') as Procedure;
 
   late final Library coreLibrary = index.getLibrary('dart:core');
 
diff --git a/pkg/kernel/lib/target/targets.dart b/pkg/kernel/lib/target/targets.dart
index 2f4eba5..6069ec8 100644
--- a/pkg/kernel/lib/target/targets.dart
+++ b/pkg/kernel/lib/target/targets.dart
@@ -252,7 +252,7 @@
       // transformations.
       Map<String, String> environmentDefines,
       DiagnosticReporter diagnosticReporter,
-      ReferenceFromIndex referenceFromIndex,
+      ReferenceFromIndex? referenceFromIndex,
       {void logger(String msg),
       ChangedStructureNotifier changedStructureNotifier});
 
@@ -262,7 +262,12 @@
   /// purposes. It is illegal to modify any of the enclosing nodes of the
   /// procedure.
   void performTransformationsOnProcedure(
-      CoreTypes coreTypes, ClassHierarchy hierarchy, Procedure procedure,
+      CoreTypes coreTypes,
+      ClassHierarchy hierarchy,
+      Procedure procedure,
+      // TODO(askesc): Consider how to generally pass compiler options to
+      // transformations.
+      Map<String, String> environmentDefines,
       {void Function(String msg)? logger}) {}
 
   /// Whether a platform library may define a restricted type, such as `bool`,
@@ -463,7 +468,7 @@
       List<Library> libraries,
       Map<String, String> environmentDefines,
       DiagnosticReporter diagnosticReporter,
-      ReferenceFromIndex referenceFromIndex,
+      ReferenceFromIndex? referenceFromIndex,
       {void Function(String msg)? logger,
       ChangedStructureNotifier? changedStructureNotifier}) {}
 
diff --git a/pkg/kernel/lib/transformations/async.dart b/pkg/kernel/lib/transformations/async.dart
index 736c9cf..f7b74c7 100644
--- a/pkg/kernel/lib/transformations/async.dart
+++ b/pkg/kernel/lib/transformations/async.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.transformations.async;
 
 import '../kernel.dart';
@@ -83,7 +81,7 @@
 
   ExpressionLifter(this.continuationRewriter);
 
-  StaticTypeContext get _staticTypeContext =>
+  StatefulStaticTypeContext get _staticTypeContext =>
       continuationRewriter.staticTypeContext;
 
   Block blockOf(List<Statement> statements) {
@@ -100,7 +98,7 @@
     assert(statements.isEmpty);
     var saved = seenAwait;
     seenAwait = false;
-    Expression result = expression.accept<TreeNode>(this);
+    Expression result = transform(expression);
     outer.addAll(statements.reversed);
     statements.clear();
     seenAwait = seenAwait || saved;
@@ -224,18 +222,18 @@
 
   TreeNode visitPropertySet(PropertySet expr) {
     return transformTreeNode(expr, () {
-      expr.value = expr.value.accept<TreeNode>(this)..parent = expr;
-      expr.receiver = expr.receiver.accept<TreeNode>(this)..parent = expr;
+      expr.value = transform(expr.value)..parent = expr;
+      expr.receiver = transform(expr.receiver)..parent = expr;
     });
   }
 
   TreeNode visitArguments(Arguments args) {
     for (var named in args.named.reversed) {
-      named.value = named.value.accept<TreeNode>(this)..parent = named;
+      named.value = transform(named.value)..parent = named;
     }
     var positional = args.positional;
     for (var i = positional.length - 1; i >= 0; --i) {
-      positional[i] = positional[i].accept<TreeNode>(this)..parent = args;
+      positional[i] = transform(positional[i])..parent = args;
     }
     // Returns the arguments, which is assumed at the call sites because they do
     // not replace the arguments or set parent pointers.
@@ -245,7 +243,7 @@
   TreeNode visitMethodInvocation(MethodInvocation expr) {
     return transformTreeNode(expr, () {
       visitArguments(expr.arguments);
-      expr.receiver = expr.receiver.accept<TreeNode>(this)..parent = expr;
+      expr.receiver = transform(expr.receiver)..parent = expr;
     });
   }
 
@@ -271,7 +269,7 @@
     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;
+        expressions[i] = transform(expressions[i])..parent = expr;
       }
     });
   }
@@ -280,8 +278,7 @@
     return transformTreeNode(expr, () {
       var expressions = expr.expressions;
       for (var i = expressions.length - 1; i >= 0; --i) {
-        expressions[i] = expr.expressions[i].accept<TreeNode>(this)
-          ..parent = expr;
+        expressions[i] = transform(expr.expressions[i])..parent = expr;
       }
     });
   }
@@ -289,8 +286,8 @@
   TreeNode visitMapLiteral(MapLiteral 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;
+        entry.value = transform(entry.value)..parent = entry;
+        entry.key = transform(entry.key)..parent = entry;
       }
     });
   }
@@ -302,16 +299,15 @@
     // Right is delimited because it is conditionally evaluated.
     var rightStatements = <Statement>[];
     seenAwait = false;
-    expr.right =
-        delimit(() => expr.right.accept<TreeNode>(this), rightStatements)
-          ..parent = expr;
+    expr.right = delimit(() => transform(expr.right), rightStatements)
+      ..parent = expr;
     var rightAwait = seenAwait;
 
     if (rightStatements.isEmpty) {
       // Easy case: right did not emit any statements.
       seenAwait = shouldName;
       return transformTreeNode(expr, () {
-        expr.left = expr.left.accept<TreeNode>(this)..parent = expr;
+        expr.left = transform(expr.left)..parent = expr;
         seenAwait = seenAwait || rightAwait;
       });
     }
@@ -352,7 +348,7 @@
     statements.add(new ExpressionStatement(new VariableSet(result, test)));
 
     seenAwait = false;
-    test.receiver = test.receiver.accept<TreeNode>(this)..parent = test;
+    test.receiver = transform(test.receiver)..parent = test;
 
     ++nameIndex;
     seenAwait = seenAwait || rightAwait;
@@ -368,7 +364,7 @@
 
     var thenStatements = <Statement>[];
     seenAwait = false;
-    expr.then = delimit(() => expr.then.accept<TreeNode>(this), thenStatements)
+    expr.then = delimit(() => transform(expr.then), thenStatements)
       ..parent = expr;
     var thenAwait = seenAwait;
 
@@ -377,9 +373,9 @@
 
     var otherwiseStatements = <Statement>[];
     seenAwait = false;
-    expr.otherwise = delimit(
-        () => expr.otherwise.accept<TreeNode>(this), otherwiseStatements)
-      ..parent = expr;
+    expr.otherwise =
+        delimit(() => transform(expr.otherwise), otherwiseStatements)
+          ..parent = expr;
     var otherwiseAwait = seenAwait;
 
     // Only one side of this branch will get executed at a time, so just make
@@ -392,7 +388,7 @@
       // Easy case: neither then nor otherwise emitted any statements.
       seenAwait = shouldName;
       return transformTreeNode(expr, () {
-        expr.condition = expr.condition.accept<TreeNode>(this)..parent = expr;
+        expr.condition = transform(expr.condition)..parent = expr;
         seenAwait = seenAwait || thenAwait || otherwiseAwait;
       });
     }
@@ -416,7 +412,7 @@
     statements.add(branch);
 
     seenAwait = false;
-    branch.condition = branch.condition.accept<TreeNode>(this)..parent = branch;
+    branch.condition = transform(branch.condition)..parent = branch;
 
     ++nameIndex;
     seenAwait = seenAwait || thenAwait || otherwiseAwait;
@@ -473,8 +469,7 @@
 
     seenAwait = false;
     var index = nameIndex;
-    arguments.positional[0] = expr.operand.accept<TreeNode>(this)
-      ..parent = arguments;
+    arguments.positional[0] = transform(expr.operand)..parent = arguments;
 
     if (shouldName && index + 1 > nameIndex) nameIndex = index + 1;
     seenAwait = true;
@@ -487,7 +482,7 @@
   }
 
   TreeNode visitLet(Let expr) {
-    var body = expr.body.accept<TreeNode>(this);
+    var body = transform(expr.body);
 
     VariableDeclaration variable = expr.variable;
     if (seenAwait) {
@@ -508,7 +503,7 @@
       statements.add(variable);
       var index = nameIndex;
       seenAwait = false;
-      variable.initializer = variable.initializer.accept<TreeNode>(this)
+      variable.initializer = transform(variable.initializer!)
         ..parent = variable;
       // Temporaries used in the initializer or the body are not live but the
       // temporary used for the body is.
@@ -521,7 +516,7 @@
       return transformTreeNode(expr, () {
         // The body has already been translated.
         expr.body = body..parent = expr;
-        variable.initializer = variable.initializer.accept<TreeNode>(this)
+        variable.initializer = transform(variable.initializer!)
           ..parent = variable;
       });
     }
@@ -530,22 +525,22 @@
   visitFunctionNode(FunctionNode node) {
     var nestedRewriter = new RecursiveContinuationRewriter(
         continuationRewriter.helper, _staticTypeContext);
-    return node.accept(nestedRewriter);
+    return nestedRewriter.transform(node);
   }
 
   TreeNode visitBlockExpression(BlockExpression expr) {
     return transformTreeNode(expr, () {
-      expr.value = expr.value.accept<TreeNode>(this)..parent = expr;
+      expr.value = transform(expr.value)..parent = expr;
       List<Statement> body = <Statement>[];
       for (Statement stmt in expr.body.statements.reversed) {
-        Statement translation = stmt.accept<TreeNode>(this);
+        Statement? translation = _rewriteStatement(stmt);
         if (translation != null) body.add(translation);
       }
       expr.body = new Block(body.reversed.toList())..parent = expr;
     });
   }
 
-  TreeNode defaultStatement(Statement stmt) {
+  Statement? _rewriteStatement(Statement stmt) {
     // This method translates a statement nested in an expression (e.g., in a
     // block expression).  It produces a translated statement, a list of
     // statements which are side effects necessary for any await, and a flag
@@ -561,7 +556,7 @@
     List<Statement> savedOuter = continuationRewriter.statements;
     statements = <Statement>[];
     continuationRewriter.statements = <Statement>[];
-    stmt.accept(continuationRewriter);
+    continuationRewriter.transform(stmt);
 
     List<Statement> results = continuationRewriter.statements;
     statements = savedInner;
@@ -570,4 +565,9 @@
     statements.addAll(results.reversed);
     return null;
   }
+
+  TreeNode defaultStatement(Statement stmt) {
+    throw new UnsupportedError(
+        "Use _rewriteStatement to transform statement: ${stmt}");
+  }
 }
diff --git a/pkg/kernel/lib/transformations/continuation.dart b/pkg/kernel/lib/transformations/continuation.dart
index 5b8e4b0..c477db5 100644
--- a/pkg/kernel/lib/transformations/continuation.dart
+++ b/pkg/kernel/lib/transformations/continuation.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.transformations.continuation;
 
 import 'dart:math' as math;
@@ -50,7 +48,7 @@
 
 void transformLibraries(
     TypeEnvironment typeEnvironment, List<Library> libraries,
-    {bool productMode}) {
+    {required bool productMode}) {
   var helper =
       new HelperNodes.fromCoreTypes(typeEnvironment.coreTypes, productMode);
   var rewriter = new RecursiveContinuationRewriter(
@@ -62,7 +60,7 @@
 
 Component transformComponent(
     TypeEnvironment typeEnvironment, Component component,
-    {bool productMode}) {
+    {required bool productMode}) {
   var helper =
       new HelperNodes.fromCoreTypes(typeEnvironment.coreTypes, productMode);
   var rewriter = new RecursiveContinuationRewriter(
@@ -72,15 +70,15 @@
 
 Procedure transformProcedure(
     TypeEnvironment typeEnvironment, Procedure procedure,
-    {bool productMode}) {
+    {required bool productMode}) {
   var helper =
       new HelperNodes.fromCoreTypes(typeEnvironment.coreTypes, productMode);
   var rewriter = new RecursiveContinuationRewriter(
       helper, new StatefulStaticTypeContext.stacked(typeEnvironment));
-  return rewriter.visitProcedure(procedure);
+  return rewriter.transform(procedure);
 }
 
-class RecursiveContinuationRewriter extends Transformer {
+class RecursiveContinuationRewriter extends RemovingTransformer {
   final HelperNodes helper;
 
   final VariableDeclaration awaitJumpVariable = new VariableDeclaration(
@@ -94,49 +92,50 @@
   RecursiveContinuationRewriter(this.helper, this.staticTypeContext);
 
   Component rewriteComponent(Component node) {
-    return node.accept<TreeNode>(this);
+    return transform(node);
   }
 
   Library rewriteLibrary(Library node) {
-    return node.accept<TreeNode>(this);
+    return transform(node);
   }
 
-  visitField(Field node) {
+  visitField(Field node, TreeNode? removalSentinel) {
     staticTypeContext.enterMember(node);
-    final result = super.visitField(node);
+    final result = super.visitField(node, removalSentinel);
     staticTypeContext.leaveMember(node);
     return result;
   }
 
-  visitConstructor(Constructor node) {
+  visitConstructor(Constructor node, TreeNode? removalSentinel) {
     staticTypeContext.enterMember(node);
-    final result = super.visitConstructor(node);
+    final result = super.visitConstructor(node, removalSentinel);
     staticTypeContext.leaveMember(node);
     return result;
   }
 
   @override
-  visitProcedure(Procedure node) {
+  visitProcedure(Procedure node, TreeNode? removalSentinel) {
     staticTypeContext.enterMember(node);
-    final result = node.isAbstract ? node : super.visitProcedure(node);
+    final result =
+        node.isAbstract ? node : super.visitProcedure(node, removalSentinel);
     staticTypeContext.leaveMember(node);
     return result;
   }
 
   @override
-  visitLibrary(Library node) {
+  visitLibrary(Library node, TreeNode? removalSentinel) {
     staticTypeContext.enterLibrary(node);
-    Library result = super.visitLibrary(node);
+    Library result = super.visitLibrary(node, removalSentinel) as Library;
     staticTypeContext.leaveLibrary(node);
     return result;
   }
 
   @override
-  visitFunctionNode(FunctionNode node) {
+  visitFunctionNode(FunctionNode node, TreeNode? removalSentinel) {
     switch (node.asyncMarker) {
       case AsyncMarker.Sync:
       case AsyncMarker.SyncYielding:
-        node.transformChildren(
+        node.transformOrRemoveChildren(
             new RecursiveContinuationRewriter(helper, staticTypeContext));
         return node;
       case AsyncMarker.SyncStar:
@@ -148,15 +147,13 @@
       case AsyncMarker.AsyncStar:
         return new AsyncStarFunctionRewriter(helper, node, staticTypeContext)
             .rewrite();
-      default:
-        return null;
     }
   }
 
   @override
-  TreeNode visitForInStatement(ForInStatement stmt) {
+  TreeNode visitForInStatement(ForInStatement stmt, TreeNode? removalSentinel) {
     if (stmt.isAsync) {
-      return super.visitForInStatement(stmt);
+      return super.visitForInStatement(stmt, removalSentinel);
     }
 
     // Transform
@@ -192,7 +189,7 @@
     assert(const [
       Nullability.nonNullable,
       Nullability.legacy
-    ].contains(coreTypes.iterableGetIterator.function.returnType.nullability));
+    ].contains(coreTypes.iterableGetIterator.function!.returnType.nullability));
 
     final DartType elementType = stmt.getElementType(staticTypeContext);
     final iteratorType = InterfaceType(
@@ -217,8 +214,8 @@
 
     final Block body = Block([variable, stmt.body]);
 
-    return Block([syncForIterator, ForStatement([], condition, [], body)])
-        .accept<TreeNode>(this);
+    return transform(
+        Block([syncForIterator, ForStatement([], condition, [], body)]));
   }
 }
 
@@ -270,38 +267,41 @@
   DartType elementTypeFromAsyncReturnType() =>
       elementTypeFromFutureOr(enclosingFunction.returnType);
 
-  Statement createContinuationPoint([Expression value]) {
+  Statement createContinuationPoint([Expression? value]) {
     if (value == null) value = new NullLiteral();
     capturedTryDepth = math.max(capturedTryDepth, currentTryDepth);
     capturedCatchDepth = math.max(capturedCatchDepth, currentCatchDepth);
     return new YieldStatement(value, isNative: true);
   }
 
-  TreeNode visitTryCatch(TryCatch node) {
+  TreeNode visitTryCatch(TryCatch node, TreeNode? removalSentinel) {
+    // ignore: unnecessary_null_comparison
     if (node.body != null) {
       ++currentTryDepth;
-      node.body = node.body.accept<TreeNode>(this);
-      node.body?.parent = node;
+      node.body = transform(node.body);
+      node.body.parent = node;
       --currentTryDepth;
     }
 
     ++currentCatchDepth;
-    transformList(node.catches, node);
+    transformCatchList(node.catches, node);
     --currentCatchDepth;
     return node;
   }
 
-  TreeNode visitTryFinally(TryFinally node) {
+  TreeNode visitTryFinally(TryFinally node, TreeNode? removalSentinel) {
+    // ignore: unnecessary_null_comparison
     if (node.body != null) {
       ++currentTryDepth;
-      node.body = node.body.accept<TreeNode>(this);
-      node.body?.parent = node;
+      node.body = transform(node.body);
+      node.body.parent = node;
       --currentTryDepth;
     }
+    // ignore: unnecessary_null_comparison
     if (node.finalizer != null) {
       ++currentCatchDepth;
-      node.finalizer = node.finalizer.accept<TreeNode>(this);
-      node.finalizer?.parent = node;
+      node.finalizer = transform(node.finalizer);
+      node.finalizer.parent = node;
       --currentCatchDepth;
     }
     return node;
@@ -334,7 +334,7 @@
 // unique to given sub-closure to prevent shared variables being overwritten.
 class ShadowRewriter extends Transformer {
   final FunctionNode enclosingFunction;
-  Map<VariableDeclaration, VariableDeclaration> _shadowedParameters = {};
+  Map<VariableDeclaration, VariableDeclaration?> _shadowedParameters = {};
 
   ShadowRewriter(this.enclosingFunction) {
     for (final parameter in enclosingFunction.positionalParameters
@@ -347,32 +347,33 @@
 
   // Return all used parameters.
   Iterable<VariableDeclaration> get shadowedParameters =>
-      _shadowedParameters.values.where((e) => e != null);
+      _shadowedParameters.values.whereType<VariableDeclaration>();
 
   VariableDeclaration _rewrite(VariableDeclaration variable) {
     if (_shadowedParameters.containsKey(variable)) {
       // Fill in placeholder.
-      if (_shadowedParameters[variable] == null) {
-        _shadowedParameters[variable] = VariableDeclaration(
+      VariableDeclaration? placeholder = _shadowedParameters[variable];
+      if (placeholder == null) {
+        placeholder = _shadowedParameters[variable] = VariableDeclaration(
           variable.name,
           type: variable.type,
           initializer: VariableGet(variable),
         );
       }
-      variable = _shadowedParameters[variable];
+      variable = placeholder;
     }
     return variable;
   }
 
   @override
   TreeNode visitVariableGet(VariableGet node) {
-    node = super.visitVariableGet(node);
+    node = super.visitVariableGet(node) as VariableGet;
     return node..variable = _rewrite(node.variable);
   }
 
   @override
   TreeNode visitVariableSet(VariableSet node) {
-    node = super.visitVariableSet(node);
+    node = super.visitVariableSet(node) as VariableSet;
     return node..variable = _rewrite(node.variable);
   }
 }
@@ -400,8 +401,8 @@
     // initialised to the original parameter values) and rewrite
     // the body to use these variables instead.
     final shadowRewriter = ShadowRewriter(enclosingFunction);
-    enclosingFunction.body =
-        enclosingFunction.body.accept<TreeNode>(shadowRewriter);
+    enclosingFunction.body = shadowRewriter.transform(enclosingFunction.body!)
+      ..parent = enclosingFunction;
 
     // TODO(cskau): Figure out why inlining this below causes segfaults.
     // Maybe related to http://dartbug.com/41596 ?
@@ -463,9 +464,8 @@
             ContinuationRewriterBase.elementTypeFrom(
                 helper.iterableClass, enclosingFunction.returnType)
           ]))),
-    ]);
-
-    enclosingFunction.body.parent = enclosingFunction;
+    ])
+      ..parent = enclosingFunction;
     enclosingFunction.asyncMarker = AsyncMarker.Sync;
 
     return enclosingFunction;
@@ -477,14 +477,14 @@
     //    :iterator.isYieldEach=
     // and return `true` as long as it did something and `false` when it's done.
     return new Block(<Statement>[
-      enclosingFunction.body.accept<TreeNode>(this),
+      transform(enclosingFunction.body!),
       new ReturnStatement(new BoolLiteral(false))
         ..fileOffset = enclosingFunction.fileEndOffset
     ]);
   }
 
-  visitYieldStatement(YieldStatement node) {
-    Expression transformedExpression = node.expression.accept<TreeNode>(this);
+  visitYieldStatement(YieldStatement node, TreeNode? removalSentinel) {
+    Expression transformedExpression = transform(node.expression);
 
     var statements = <Statement>[];
     if (node.isYieldStar) {
@@ -506,7 +506,8 @@
     return new Block(statements);
   }
 
-  TreeNode visitReturnStatement(ReturnStatement node) {
+  TreeNode visitReturnStatement(
+      ReturnStatement node, TreeNode? removalSentinel) {
     // sync* functions cannot return a value.
     assert(node.expression == null || node.expression is NullLiteral);
     node.expression = new BoolLiteral(false)..parent = node;
@@ -524,12 +525,12 @@
   // :async_op_error has type (Object e, StackTrace s) -> dynamic
   final VariableDeclaration catchErrorContinuationVariable;
 
-  LabeledStatement labeledBody;
+  LabeledStatement? labeledBody;
 
-  ExpressionLifter expressionRewriter;
+  ExpressionLifter? expressionRewriter;
 
   AsyncRewriterBase(HelperNodes helper, FunctionNode enclosingFunction,
-      StaticTypeContext staticTypeContext)
+      StatefulStaticTypeContext staticTypeContext)
       : nestedClosureVariable = VariableDeclaration(
             ContinuationVariables.asyncOp,
             type: FunctionType([
@@ -564,7 +565,7 @@
     //     modified <node.body>;
     // }
     final parameters = <VariableDeclaration>[
-      expressionRewriter.asyncResult,
+      expressionRewriter!.asyncResult,
       new VariableDeclaration(ContinuationVariables.exceptionParam),
       new VariableDeclaration(ContinuationVariables.stackTraceParam),
     ];
@@ -585,12 +586,12 @@
     // TODO(kustermann): If we didn't need any variables we should not emit
     // these.
     statements.addAll(variableDeclarations());
-    statements.addAll(expressionRewriter.variables);
+    statements.addAll(expressionRewriter!.variables);
 
     // Now add the closure function itself.
     final closureFunction =
         new FunctionDeclaration(nestedClosureVariable, function)
-          ..fileOffset = enclosingFunction.parent.fileOffset;
+          ..fileOffset = enclosingFunction.parent!.fileOffset;
     statements.add(closureFunction);
 
     // :async_op_then = _asyncThenWrapperHelper(asyncBody);
@@ -613,7 +614,7 @@
   Statement buildWrappedBody() {
     ++currentTryDepth;
     labeledBody = new LabeledStatement(null);
-    labeledBody.body = visitDelimited(enclosingFunction.body)
+    labeledBody!.body = visitDelimited(enclosingFunction.body!)
       ..parent = labeledBody;
     --currentTryDepth;
 
@@ -623,7 +624,7 @@
             helper.coreTypes.stackTraceRawType(staticTypeContext.nonNullable));
 
     return new TryCatch(
-      buildReturn(labeledBody),
+      buildReturn(labeledBody!),
       <Catch>[
         new Catch(
             exceptionVariable,
@@ -636,63 +637,65 @@
     );
   }
 
-  Statement buildCatchBody(
-      Statement exceptionVariable, Statement stackTraceVariable);
+  Statement buildCatchBody(VariableDeclaration exceptionVariable,
+      VariableDeclaration stackTraceVariable);
 
   Statement buildReturn(Statement body);
 
   List<Statement> statements = <Statement>[];
 
-  TreeNode visitExpressionStatement(ExpressionStatement stmt) {
-    stmt.expression = expressionRewriter.rewrite(stmt.expression, statements)
+  TreeNode visitExpressionStatement(
+      ExpressionStatement stmt, TreeNode? removalSentinel) {
+    stmt.expression = expressionRewriter!.rewrite(stmt.expression, statements)
       ..parent = stmt;
     statements.add(stmt);
-    return null;
+    return removalSentinel ?? EmptyStatement();
   }
 
-  TreeNode visitBlock(Block stmt) {
+  TreeNode visitBlock(Block stmt, TreeNode? removalSentinel) {
     var saved = statements;
     statements = <Statement>[];
     for (var statement in stmt.statements) {
-      statement.accept<TreeNode>(this);
+      transform(statement);
     }
     saved.add(new Block(statements));
     statements = saved;
-    return null;
+    return removalSentinel ?? EmptyStatement();
   }
 
-  TreeNode visitEmptyStatement(EmptyStatement stmt) {
+  TreeNode visitEmptyStatement(EmptyStatement stmt, TreeNode? removalSentinel) {
     statements.add(stmt);
-    return null;
+    return removalSentinel ?? EmptyStatement();
   }
 
-  TreeNode visitAssertBlock(AssertBlock stmt) {
+  TreeNode visitAssertBlock(AssertBlock stmt, TreeNode? removalSentinel) {
     var saved = statements;
     statements = <Statement>[];
     for (var statement in stmt.statements) {
-      statement.accept<TreeNode>(this);
+      transform(statement);
     }
     saved.add(new Block(statements));
     statements = saved;
-    return null;
+    return removalSentinel ?? EmptyStatement();
   }
 
-  TreeNode visitAssertStatement(AssertStatement stmt) {
+  TreeNode visitAssertStatement(
+      AssertStatement stmt, TreeNode? removalSentinel) {
     var condEffects = <Statement>[];
-    var cond = expressionRewriter.rewrite(stmt.condition, condEffects);
+    var cond = expressionRewriter!.rewrite(stmt.condition, condEffects);
     if (stmt.message == null) {
       stmt.condition = cond..parent = stmt;
       // If the translation of the condition produced a non-empty list of
       // statements, ensure they are guarded by whether asserts are enabled.
       statements.add(
           condEffects.isEmpty ? stmt : new AssertBlock(condEffects..add(stmt)));
-      return null;
+      return removalSentinel ?? EmptyStatement();
     }
 
     // The translation depends on the translation of the message, by cases.
     Statement result;
     var msgEffects = <Statement>[];
-    stmt.message = expressionRewriter.rewrite(stmt.message, msgEffects)
+    stmt.message = expressionRewriter!.rewrite(stmt.message!, msgEffects)
       ..parent = stmt;
     if (condEffects.isEmpty) {
       if (msgEffects.isEmpty) {
@@ -733,34 +736,35 @@
       result = new AssertBlock(condEffects);
     }
     statements.add(result);
-    return null;
+    return removalSentinel ?? EmptyStatement();
   }
 
   Statement visitDelimited(Statement stmt) {
     var saved = statements;
     statements = <Statement>[];
-    stmt.accept<TreeNode>(this);
+    transform(stmt);
     Statement result =
         statements.length == 1 ? statements.first : new Block(statements);
     statements = saved;
     return result;
   }
 
-  Statement visitLabeledStatement(LabeledStatement stmt) {
+  TreeNode visitLabeledStatement(
+      LabeledStatement stmt, TreeNode? removalSentinel) {
     stmt.body = visitDelimited(stmt.body)..parent = stmt;
     statements.add(stmt);
-    return null;
+    return removalSentinel ?? EmptyStatement();
   }
 
-  Statement visitBreakStatement(BreakStatement stmt) {
+  TreeNode visitBreakStatement(BreakStatement stmt, TreeNode? removalSentinel) {
     statements.add(stmt);
-    return null;
+    return removalSentinel ?? EmptyStatement();
   }
 
-  TreeNode visitWhileStatement(WhileStatement stmt) {
+  TreeNode visitWhileStatement(WhileStatement stmt, TreeNode? removalSentinel) {
     Statement body = visitDelimited(stmt.body);
     List<Statement> effects = <Statement>[];
-    Expression cond = expressionRewriter.rewrite(stmt.condition, effects);
+    Expression cond = expressionRewriter!.rewrite(stmt.condition, effects);
     if (effects.isEmpty) {
       stmt.condition = cond..parent = stmt;
       stmt.body = body..parent = stmt;
@@ -783,13 +787,13 @@
       stmt.body = new Block(effects)..parent = stmt;
       statements.add(labeled);
     }
-    return null;
+    return removalSentinel ?? EmptyStatement();
   }
 
-  TreeNode visitDoStatement(DoStatement stmt) {
+  TreeNode visitDoStatement(DoStatement stmt, TreeNode? removalSentinel) {
     Statement body = visitDelimited(stmt.body);
     List<Statement> effects = <Statement>[];
-    stmt.condition = expressionRewriter.rewrite(stmt.condition, effects)
+    stmt.condition = expressionRewriter!.rewrite(stmt.condition, effects)
       ..parent = stmt;
     if (effects.isNotEmpty) {
       // The condition rewrote to a non-empty sequence of statements S* and
@@ -802,44 +806,43 @@
     }
     stmt.body = body..parent = stmt;
     statements.add(stmt);
-    return null;
+    return removalSentinel ?? EmptyStatement();
   }
 
-  TreeNode visitForStatement(ForStatement stmt) {
+  TreeNode visitForStatement(ForStatement stmt, TreeNode? removalSentinel) {
     // Because of for-loop scoping and variable capture, it is tricky to deal
     // with await in the loop's variable initializers or update expressions.
     bool isSimple = true;
     int length = stmt.variables.length;
     List<List<Statement>> initEffects =
-        new List<List<Statement>>.filled(length, null);
-    for (int i = 0; i < length; ++i) {
+        new List<List<Statement>>.generate(length, (int i) {
       VariableDeclaration decl = stmt.variables[i];
-      initEffects[i] = <Statement>[];
+      List<Statement> statements = <Statement>[];
       if (decl.initializer != null) {
-        decl.initializer = expressionRewriter.rewrite(
-            decl.initializer, initEffects[i])
-          ..parent = decl;
+        decl.initializer = expressionRewriter!
+            .rewrite(decl.initializer!, statements)
+              ..parent = decl;
       }
-      isSimple = isSimple && initEffects[i].isEmpty;
-    }
+      isSimple = isSimple && statements.isEmpty;
+      return statements;
+    });
 
     length = stmt.updates.length;
     List<List<Statement>> updateEffects =
-        new List<List<Statement>>.filled(length, null);
-    for (int i = 0; i < length; ++i) {
-      updateEffects[i] = <Statement>[];
-      stmt.updates[i] = expressionRewriter.rewrite(
-          stmt.updates[i], updateEffects[i])
+        new List<List<Statement>>.generate(length, (int i) {
+      List<Statement> statements = <Statement>[];
+      stmt.updates[i] = expressionRewriter!.rewrite(stmt.updates[i], statements)
         ..parent = stmt;
-      isSimple = isSimple && updateEffects[i].isEmpty;
-    }
+      isSimple = isSimple && statements.isEmpty;
+      return statements;
+    });
 
     Statement body = visitDelimited(stmt.body);
-    Expression cond = stmt.condition;
-    List<Statement> condEffects;
+    Expression? cond = stmt.condition;
+    List<Statement>? condEffects;
     if (cond != null) {
       condEffects = <Statement>[];
-      cond = expressionRewriter.rewrite(stmt.condition, condEffects);
+      cond = expressionRewriter!.rewrite(stmt.condition!, condEffects);
     }
 
     if (isSimple) {
@@ -855,11 +858,11 @@
         // No condition in a for loop is the same as true.
         stmt.condition = null;
         condEffects
-            .add(new IfStatement(cond, body, new BreakStatement(labeled)));
+            .add(new IfStatement(cond!, body, new BreakStatement(labeled)));
         stmt.body = new Block(condEffects)..parent = stmt;
         statements.add(labeled);
       }
-      return null;
+      return removalSentinel ?? EmptyStatement();
     }
 
     // If the rewrite of the initializer or update expressions produces a
@@ -915,7 +918,7 @@
       if (decl.initializer != null) {
         initializers.addAll(initEffects[i]);
         initializers.add(
-            new ExpressionStatement(new VariableSet(decl, decl.initializer)));
+            new ExpressionStatement(new VariableSet(decl, decl.initializer!)));
         decl.initializer = null;
       }
       updates.add(new ExpressionStatement(
@@ -934,7 +937,7 @@
 
     LabeledStatement labeled = new LabeledStatement(null);
     if (cond != null) {
-      loopBody.addAll(condEffects);
+      loopBody.addAll(condEffects!);
     } else {
       cond = new BoolLiteral(true);
     }
@@ -946,10 +949,10 @@
     statements.add(new Block(<Statement>[]
       ..addAll(temps)
       ..add(labeled)));
-    return null;
+    return removalSentinel ?? EmptyStatement();
   }
 
-  TreeNode visitForInStatement(ForInStatement stmt) {
+  TreeNode visitForInStatement(ForInStatement stmt, TreeNode? removalSentinel) {
     if (stmt.isAsync) {
       // Transform
       //
@@ -1032,7 +1035,7 @@
           new Name('current'),
           helper.streamIteratorCurrent)
         ..fileOffset = stmt.bodyOffset;
-      valueVariable.initializer.parent = valueVariable;
+      valueVariable.initializer!.parent = valueVariable;
 
       var whileBody = new Block(<Statement>[valueVariable, stmt.body]);
       var tryBody = new WhileStatement(whileCondition, whileBody);
@@ -1058,16 +1061,17 @@
 
       var block = new Block(
           <Statement>[streamVariable, forIteratorVariable, tryFinally]);
-      block.accept<TreeNode>(this);
-      return null;
+      transform<Statement>(block);
+      return removalSentinel ?? EmptyStatement();
     } else {
-      super.visitForInStatement(stmt);
-      return null;
+      super.visitForInStatement(stmt, removalSentinel);
+      return removalSentinel ?? EmptyStatement();
     }
   }
 
-  TreeNode visitSwitchStatement(SwitchStatement stmt) {
-    stmt.expression = expressionRewriter.rewrite(stmt.expression, statements)
+  TreeNode visitSwitchStatement(
+      SwitchStatement stmt, TreeNode? removalSentinel) {
+    stmt.expression = expressionRewriter!.rewrite(stmt.expression, statements)
       ..parent = stmt;
     for (var switchCase in stmt.cases) {
       // Expressions in switch cases cannot contain await so they do not need to
@@ -1075,26 +1079,27 @@
       switchCase.body = visitDelimited(switchCase.body)..parent = switchCase;
     }
     statements.add(stmt);
-    return null;
+    return removalSentinel ?? EmptyStatement();
   }
 
-  TreeNode visitContinueSwitchStatement(ContinueSwitchStatement stmt) {
+  TreeNode visitContinueSwitchStatement(
+      ContinueSwitchStatement stmt, TreeNode? removalSentinel) {
     statements.add(stmt);
-    return null;
+    return removalSentinel ?? EmptyStatement();
   }
 
-  TreeNode visitIfStatement(IfStatement stmt) {
-    stmt.condition = expressionRewriter.rewrite(stmt.condition, statements)
+  TreeNode visitIfStatement(IfStatement stmt, TreeNode? removalSentinel) {
+    stmt.condition = expressionRewriter!.rewrite(stmt.condition, statements)
       ..parent = stmt;
     stmt.then = visitDelimited(stmt.then)..parent = stmt;
     if (stmt.otherwise != null) {
-      stmt.otherwise = visitDelimited(stmt.otherwise)..parent = stmt;
+      stmt.otherwise = visitDelimited(stmt.otherwise!)..parent = stmt;
     }
     statements.add(stmt);
-    return null;
+    return removalSentinel ?? EmptyStatement();
   }
 
-  TreeNode visitTryCatch(TryCatch stmt) {
+  TreeNode visitTryCatch(TryCatch stmt, TreeNode? removalSentinel) {
     ++currentTryDepth;
     stmt.body = visitDelimited(stmt.body)..parent = stmt;
     --currentTryDepth;
@@ -1105,10 +1110,10 @@
     }
     --currentCatchDepth;
     statements.add(stmt);
-    return null;
+    return removalSentinel ?? EmptyStatement();
   }
 
-  TreeNode visitTryFinally(TryFinally stmt) {
+  TreeNode visitTryFinally(TryFinally stmt, TreeNode? removalSentinel) {
     ++currentTryDepth;
     stmt.body = visitDelimited(stmt.body)..parent = stmt;
     --currentTryDepth;
@@ -1116,40 +1121,43 @@
     stmt.finalizer = visitDelimited(stmt.finalizer)..parent = stmt;
     --currentCatchDepth;
     statements.add(stmt);
-    return null;
+    return removalSentinel ?? EmptyStatement();
   }
 
-  TreeNode visitYieldStatement(YieldStatement stmt) {
-    stmt.expression = expressionRewriter.rewrite(stmt.expression, statements)
+  TreeNode visitYieldStatement(YieldStatement stmt, TreeNode? removalSentinel) {
+    stmt.expression = expressionRewriter!.rewrite(stmt.expression, statements)
       ..parent = stmt;
     statements.add(stmt);
-    return null;
+    return removalSentinel ?? EmptyStatement();
   }
 
-  TreeNode visitVariableDeclaration(VariableDeclaration stmt) {
+  TreeNode visitVariableDeclaration(
+      VariableDeclaration stmt, TreeNode? removalSentinel) {
     if (stmt.initializer != null) {
-      stmt.initializer = expressionRewriter.rewrite(
-          stmt.initializer, statements)
-        ..parent = stmt;
+      stmt.initializer = expressionRewriter!
+          .rewrite(stmt.initializer!, statements)
+            ..parent = stmt;
     }
     statements.add(stmt);
-    return null;
+    return removalSentinel ?? EmptyStatement();
   }
 
-  TreeNode visitFunctionDeclaration(FunctionDeclaration stmt) {
-    stmt.function = stmt.function.accept<TreeNode>(this)..parent = stmt;
+  TreeNode visitFunctionDeclaration(
+      FunctionDeclaration stmt, TreeNode? removalSentinel) {
+    stmt.function = transform(stmt.function!)..parent = stmt;
     statements.add(stmt);
-    return null;
+    return removalSentinel ?? EmptyStatement();
   }
 
-  defaultExpression(TreeNode node) => throw 'unreachable $node';
+  defaultExpression(TreeNode node, TreeNode? removalSentinel) =>
+      throw 'unreachable $node';
 }
 
 class AsyncStarFunctionRewriter extends AsyncRewriterBase {
-  VariableDeclaration controllerVariable;
+  VariableDeclaration? controllerVariable;
 
   AsyncStarFunctionRewriter(HelperNodes helper, FunctionNode enclosingFunction,
-      StaticTypeContext staticTypeContext)
+      StatefulStaticTypeContext staticTypeContext)
       : super(helper, enclosingFunction, staticTypeContext);
 
   FunctionNode rewrite() {
@@ -1162,7 +1170,7 @@
         ContinuationVariables.controller,
         type: new InterfaceType(helper.asyncStarStreamControllerClass,
             staticTypeContext.nullable, [elementType]));
-    statements.add(controllerVariable);
+    statements.add(controllerVariable!);
 
     // dynamic :controller_stream;
     VariableDeclaration controllerStreamVariable =
@@ -1179,11 +1187,11 @@
         helper.asyncStarStreamControllerConstructor, arguments)
       ..fileOffset = enclosingFunction.fileOffset;
     var setController = new ExpressionStatement(
-        new VariableSet(controllerVariable, buildController));
+        new VariableSet(controllerVariable!, buildController));
     statements.add(setController);
 
     // :controller_stream = :controller.stream;
-    var completerGet = new VariableGet(controllerVariable);
+    var completerGet = new VariableGet(controllerVariable!);
     statements.add(new ExpressionStatement(new VariableSet(
         controllerStreamVariable,
         new PropertyGet(completerGet, new Name('stream', helper.asyncLibrary),
@@ -1194,8 +1202,7 @@
         new ReturnStatement(new VariableGet(controllerStreamVariable));
     statements.add(returnStatement);
 
-    enclosingFunction.body = new Block(statements);
-    enclosingFunction.body.parent = enclosingFunction;
+    enclosingFunction.body = new Block(statements)..parent = enclosingFunction;
     enclosingFunction.asyncMarker = AsyncMarker.Sync;
     return enclosingFunction;
   }
@@ -1206,7 +1213,7 @@
     --currentTryDepth;
 
     var finallyBody = new ExpressionStatement(new MethodInvocation(
-        new VariableGet(controllerVariable),
+        new VariableGet(controllerVariable!),
         new Name('close'),
         new Arguments(<Expression>[]),
         helper.asyncStarStreamControllerClose));
@@ -1215,9 +1222,10 @@
     return tryFinally;
   }
 
-  Statement buildCatchBody(exceptionVariable, stackTraceVariable) {
+  Statement buildCatchBody(VariableDeclaration exceptionVariable,
+      VariableDeclaration stackTraceVariable) {
     return new ExpressionStatement(new MethodInvocation(
-        new VariableGet(controllerVariable),
+        new VariableGet(controllerVariable!),
         new Name('addError'),
         new Arguments(<Expression>[
           new VariableGet(exceptionVariable),
@@ -1235,11 +1243,11 @@
     ]);
   }
 
-  TreeNode visitYieldStatement(YieldStatement stmt) {
-    Expression expr = expressionRewriter.rewrite(stmt.expression, statements);
+  TreeNode visitYieldStatement(YieldStatement stmt, TreeNode? removalSentinel) {
+    Expression expr = expressionRewriter!.rewrite(stmt.expression, statements);
 
     var addExpression = new MethodInvocation(
-        new VariableGet(controllerVariable),
+        new VariableGet(controllerVariable!),
         new Name(stmt.isYieldStar ? 'addStream' : 'add', helper.asyncLibrary),
         new Arguments(<Expression>[expr]),
         stmt.isYieldStar
@@ -1251,25 +1259,26 @@
         addExpression,
         new ReturnStatement(new NullLiteral()),
         createContinuationPoint()..fileOffset = stmt.fileOffset));
-    return null;
+    return removalSentinel ?? EmptyStatement();
   }
 
-  TreeNode visitReturnStatement(ReturnStatement node) {
+  TreeNode visitReturnStatement(
+      ReturnStatement node, TreeNode? removalSentinel) {
     // Async* functions cannot return a value.
     assert(node.expression == null || node.expression is NullLiteral);
     statements
-        .add(new BreakStatement(labeledBody)..fileOffset = node.fileOffset);
-    return null;
+        .add(new BreakStatement(labeledBody!)..fileOffset = node.fileOffset);
+    return removalSentinel ?? EmptyStatement();
   }
 }
 
 class AsyncFunctionRewriter extends AsyncRewriterBase {
-  VariableDeclaration returnVariable;
-  VariableDeclaration asyncFutureVariable;
-  VariableDeclaration isSyncVariable;
+  VariableDeclaration? returnVariable;
+  VariableDeclaration? asyncFutureVariable;
+  VariableDeclaration? isSyncVariable;
 
   AsyncFunctionRewriter(HelperNodes helper, FunctionNode enclosingFunction,
-      StaticTypeContext staticTypeContext)
+      StatefulStaticTypeContext staticTypeContext)
       : super(helper, enclosingFunction, staticTypeContext);
 
   FunctionNode rewrite() {
@@ -1298,18 +1307,18 @@
           ..fileOffset = enclosingFunction.body?.fileOffset ?? -1,
         isFinal: true,
         type: futureType);
-    statements.add(asyncFutureVariable);
+    statements.add(asyncFutureVariable!);
 
     // bool :is_sync = false;
     isSyncVariable = VariableDeclaration(ContinuationVariables.isSync,
         initializer: BoolLiteral(false),
         type: helper.coreTypes.boolLegacyRawType);
-    statements.add(isSyncVariable);
+    statements.add(isSyncVariable!);
 
     // asy::FutureOr<dynamic>* :return_value;
     returnVariable = VariableDeclaration(ContinuationVariables.returnValue,
         type: returnType);
-    statements.add(returnVariable);
+    statements.add(returnVariable!);
 
     setupAsyncContinuations(statements);
 
@@ -1323,14 +1332,13 @@
 
     // :is_sync = true;
     final setIsSync =
-        ExpressionStatement(VariableSet(isSyncVariable, BoolLiteral(true)));
+        ExpressionStatement(VariableSet(isSyncVariable!, BoolLiteral(true)));
     statements.add(setIsSync);
 
     // return :async_future;
-    statements.add(ReturnStatement(VariableGet(asyncFutureVariable)));
+    statements.add(ReturnStatement(VariableGet(asyncFutureVariable!)));
 
-    enclosingFunction.body = Block(statements);
-    enclosingFunction.body.parent = enclosingFunction;
+    enclosingFunction.body = Block(statements)..parent = enclosingFunction;
     enclosingFunction.asyncMarker = AsyncMarker.Sync;
     return enclosingFunction;
   }
@@ -1341,10 +1349,10 @@
     return ExpressionStatement(StaticInvocation(
         helper.completeOnAsyncError,
         Arguments([
-          VariableGet(asyncFutureVariable),
+          VariableGet(asyncFutureVariable!),
           VariableGet(exceptionVariable),
           VariableGet(stackTraceVariable),
-          VariableGet(isSyncVariable)
+          VariableGet(isSyncVariable!)
         ])));
   }
 
@@ -1361,22 +1369,22 @@
       ExpressionStatement(StaticInvocation(
           helper.completeOnAsyncReturn,
           Arguments([
-            VariableGet(asyncFutureVariable),
-            VariableGet(returnVariable),
-            VariableGet(isSyncVariable)
+            VariableGet(asyncFutureVariable!),
+            VariableGet(returnVariable!),
+            VariableGet(isSyncVariable!)
           ]))),
       ReturnStatement()..fileOffset = enclosingFunction.fileEndOffset
     ]);
   }
 
-  visitReturnStatement(ReturnStatement node) {
+  visitReturnStatement(ReturnStatement node, TreeNode? removalSentinel) {
     var expr = node.expression == null
         ? new NullLiteral()
-        : expressionRewriter.rewrite(node.expression, statements);
+        : expressionRewriter!.rewrite(node.expression!, statements);
     statements.add(new ExpressionStatement(
-        new VariableSet(returnVariable, expr)..fileOffset = node.fileOffset));
-    statements.add(new BreakStatement(labeledBody));
-    return null;
+        new VariableSet(returnVariable!, expr)..fileOffset = node.fileOffset));
+    statements.add(new BreakStatement(labeledBody!));
+    return removalSentinel ?? EmptyStatement();
   }
 }
 
@@ -1390,11 +1398,11 @@
   final Member asyncStarStreamControllerClose;
   final Constructor asyncStarStreamControllerConstructor;
   final Member asyncStarStreamControllerStream;
-  final Member asyncStarMoveNextHelper;
+  final Procedure asyncStarMoveNextHelper;
   final Procedure asyncThenWrapper;
   final Procedure awaitHelper;
-  final Member completeOnAsyncReturn;
-  final Member completeOnAsyncError;
+  final Procedure completeOnAsyncReturn;
+  final Procedure completeOnAsyncError;
   final Library coreLibrary;
   final CoreTypes coreTypes;
   final Class futureClass;
@@ -1488,6 +1496,7 @@
         coreTypes.syncIteratorYieldEachIterable,
         coreTypes.boolClass,
         productMode,
-        coreTypes.index.getTopLevelMember('dart:_internal', 'unsafeCast'));
+        coreTypes.index.getTopLevelMember('dart:_internal', 'unsafeCast')
+            as Procedure);
   }
 }
diff --git a/pkg/kernel/lib/transformations/mixin_full_resolution.dart b/pkg/kernel/lib/transformations/mixin_full_resolution.dart
index d409835..c808cf3 100644
--- a/pkg/kernel/lib/transformations/mixin_full_resolution.dart
+++ b/pkg/kernel/lib/transformations/mixin_full_resolution.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.transformations.mixin_full_resolution;
 
 import '../ast.dart';
@@ -22,7 +20,7 @@
     CoreTypes coreTypes,
     ClassHierarchy hierarchy,
     List<Library> libraries,
-    ReferenceFromIndex referenceFromIndex) {
+    ReferenceFromIndex? referenceFromIndex) {
   new MixinFullResolution(targetInfo, coreTypes, hierarchy)
       .transform(libraries, referenceFromIndex);
 }
@@ -46,7 +44,7 @@
   /// Transform the given new [libraries].  It is expected that all other
   /// libraries have already been transformed.
   void transform(
-      List<Library> libraries, ReferenceFromIndex referenceFromIndex) {
+      List<Library> libraries, ReferenceFromIndex? referenceFromIndex) {
     if (libraries.isEmpty) return;
 
     var transformedClasses = new Set<Class>();
@@ -71,7 +69,7 @@
       Set<Class> processedClasses,
       Set<Class> transformedClasses,
       Class class_,
-      ReferenceFromIndex referenceFromIndex) {
+      ReferenceFromIndex? referenceFromIndex) {
     // If this class was already handled then so were all classes up to the
     // [Object] class.
     if (!processedClasses.add(class_)) return;
@@ -79,7 +77,7 @@
     Library enclosingLibrary = class_.enclosingLibrary;
 
     if (!librariesToBeTransformed.contains(enclosingLibrary) &&
-        enclosingLibrary.importUri?.scheme == "dart") {
+        enclosingLibrary.importUri.scheme == "dart") {
       // If we're not asked to transform the platform libraries then we expect
       // that they will be already transformed.
       return;
@@ -88,7 +86,7 @@
     // Ensure super classes have been transformed before this class.
     if (class_.superclass != null) {
       transformClass(librariesToBeTransformed, processedClasses,
-          transformedClasses, class_.superclass, referenceFromIndex);
+          transformedClasses, class_.superclass!, referenceFromIndex);
     }
 
     // If this is not a mixin application we don't need to make forwarding
@@ -99,12 +97,13 @@
     transformedClasses.add(class_);
 
     // Clone fields and methods from the mixin class.
-    var substitution = getSubstitutionMap(class_.mixedInType);
+    var substitution = getSubstitutionMap(class_.mixedInType!);
     var cloner = new CloneVisitorWithMembers(typeSubstitution: substitution);
 
-    IndexedLibrary indexedLibrary =
+    IndexedLibrary? indexedLibrary =
         referenceFromIndex?.lookupLibrary(enclosingLibrary);
-    IndexedClass indexedClass = indexedLibrary?.lookupIndexedClass(class_.name);
+    IndexedClass? indexedClass =
+        indexedLibrary?.lookupIndexedClass(class_.name);
 
     if (class_.mixin.fields.isNotEmpty) {
       // When we copy a field from the mixed in class, we remove any
@@ -114,17 +113,17 @@
       var setters = <Name, Procedure>{};
       for (var procedure in class_.procedures) {
         if (procedure.isSetter) {
-          setters[procedure.name] = procedure;
+          setters[procedure.name!] = procedure;
         } else {
-          nonSetters[procedure.name] = procedure;
+          nonSetters[procedure.name!] = procedure;
         }
       }
 
       for (var field in class_.mixin.fields) {
-        Reference getterReference =
-            indexedClass?.lookupGetterReference(field.name);
-        Reference setterReference =
-            indexedClass?.lookupSetterReference(field.name);
+        Reference? getterReference =
+            indexedClass?.lookupGetterReference(field.name!);
+        Reference? setterReference =
+            indexedClass?.lookupSetterReference(field.name!);
         if (getterReference == null) {
           getterReference = nonSetters[field.name]?.reference;
           getterReference?.canonicalName?.unbind();
@@ -135,11 +134,11 @@
         }
         Field clone =
             cloner.cloneField(field, getterReference, setterReference);
-        Procedure setter = setters[field.name];
+        Procedure? setter = setters[field.name!];
         if (setter != null) {
           setters.remove(field.name);
           VariableDeclaration parameter =
-              setter.function.positionalParameters.first;
+              setter.function!.positionalParameters.first;
           clone.isCovariant = parameter.isCovariant;
           clone.isGenericCovariantImpl = parameter.isGenericCovariantImpl;
         }
@@ -168,21 +167,21 @@
       // NoSuchMethod forwarders aren't cloned.
       if (procedure.isNoSuchMethodForwarder) continue;
 
-      Reference reference;
+      Reference? reference;
       if (procedure.isSetter) {
-        reference = indexedClass?.lookupSetterReference(procedure.name);
+        reference = indexedClass?.lookupSetterReference(procedure.name!);
       } else {
-        reference = indexedClass?.lookupGetterReference(procedure.name);
+        reference = indexedClass?.lookupGetterReference(procedure.name!);
       }
 
       // Linear search for a forwarding stub with the same name.
-      int originalIndex;
+      int? originalIndex;
       for (int i = 0; i < originalLength; ++i) {
         var originalProcedure = class_.procedures[i];
         if (originalProcedure.name == procedure.name &&
             originalProcedure.kind == procedure.kind) {
-          FunctionNode src = originalProcedure.function;
-          FunctionNode dst = procedure.function;
+          FunctionNode src = originalProcedure.function!;
+          FunctionNode dst = procedure.function!;
 
           if (src.positionalParameters.length !=
                   dst.positionalParameters.length ||
@@ -196,13 +195,13 @@
         }
       }
       if (originalIndex != null) {
-        reference ??= class_.procedures[originalIndex]?.reference;
+        reference ??= class_.procedures[originalIndex].reference;
       }
       Procedure clone = cloner.cloneProcedure(procedure, reference);
       if (originalIndex != null) {
         Procedure originalProcedure = class_.procedures[originalIndex];
-        FunctionNode src = originalProcedure.function;
-        FunctionNode dst = clone.function;
+        FunctionNode src = originalProcedure.function!;
+        FunctionNode dst = clone.function!;
         assert(src.typeParameters.length == dst.typeParameters.length);
         for (int j = 0; j < src.typeParameters.length; ++j) {
           dst.typeParameters[j].flags = src.typeParameters[j].flags;
@@ -229,7 +228,7 @@
 
     // This class implements the mixin type. Also, backends rely on the fact
     // that eliminated mixin is appended into the end of interfaces list.
-    class_.implementedTypes.add(class_.mixedInType);
+    class_.implementedTypes.add(class_.mixedInType!);
 
     // This class is now a normal class.
     class_.mixedInType = null;
diff --git a/pkg/kernel/lib/vm/constants_native_effects.dart b/pkg/kernel/lib/vm/constants_native_effects.dart
index 401504a..f4efb71 100644
--- a/pkg/kernel/lib/vm/constants_native_effects.dart
+++ b/pkg/kernel/lib/vm/constants_native_effects.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 vm.constants_native_effects;
 
 import '../ast.dart';
@@ -25,12 +23,13 @@
     final Library coreLibrary = coreTypes.coreLibrary;
     final Class immutableMapClass = coreLibrary.classes
         .firstWhere((Class klass) => klass.name == '_ImmutableMap');
+    // ignore: unnecessary_null_comparison
     assert(immutableMapClass != null);
     Field unmodifiableSetMap = coreTypes.index
-        .getMember('dart:collection', '_UnmodifiableSet', '_map');
+        .getMember('dart:collection', '_UnmodifiableSet', '_map') as Field;
 
     return new VmConstantsBackend._(immutableMapClass, unmodifiableSetMap,
-        unmodifiableSetMap.enclosingClass);
+        unmodifiableSetMap.enclosingClass!);
   }
 
   @override
@@ -38,12 +37,11 @@
     // The _ImmutableMap class is implemented via one field pointing to a list
     // of key/value pairs -- see runtime/lib/immutable_map.dart!
     final List<Constant> kvListPairs =
-        new List<Constant>.filled(2 * constant.entries.length, null);
-    for (int i = 0; i < constant.entries.length; i++) {
-      final ConstantMapEntry entry = constant.entries[i];
-      kvListPairs[2 * i] = entry.key;
-      kvListPairs[2 * i + 1] = entry.value;
-    }
+        new List<Constant>.generate(2 * constant.entries.length, (int i) {
+      final int index = i ~/ 2;
+      final ConstantMapEntry entry = constant.entries[index];
+      return i % 2 == 0 ? entry.key : entry.value;
+    });
     // This is a bit fishy, since we merge the key and the value type by
     // putting both into the same list.
     final ListConstant kvListConstant =
@@ -69,11 +67,11 @@
   void forEachLoweredMapConstantEntry(
       Constant constant, void Function(Constant key, Constant value) f) {
     assert(isLoweredMapConstant(constant));
-    final InstanceConstant instance = constant;
+    final InstanceConstant instance = constant as InstanceConstant;
     assert(immutableMapClass.fields.length == 1);
     final Field kvPairListField = immutableMapClass.fields[0];
     final ListConstant kvListConstant =
-        instance.fieldValues[kvPairListField.getterReference];
+        instance.fieldValues[kvPairListField.getterReference] as ListConstant;
     assert(kvListConstant.entries.length % 2 == 0);
     for (int index = 0; index < kvListConstant.entries.length; index += 2) {
       f(kvListConstant.entries[index], kvListConstant.entries[index + 1]);
@@ -85,10 +83,9 @@
     final DartType elementType = constant.typeArgument;
     final List<Constant> entries = constant.entries;
     final List<ConstantMapEntry> mapEntries =
-        new List<ConstantMapEntry>.filled(entries.length, null);
-    for (int i = 0; i < entries.length; ++i) {
-      mapEntries[i] = new ConstantMapEntry(entries[i], new NullConstant());
-    }
+        new List<ConstantMapEntry>.generate(entries.length, (int index) {
+      return new ConstantMapEntry(entries[index], new NullConstant());
+    });
     Constant map = lowerMapConstant(
         new MapConstant(elementType, const NullType(), mapEntries));
     return new InstanceConstant(unmodifiableSetClass.reference, [elementType],
@@ -101,7 +98,7 @@
         constant.classNode == unmodifiableSetClass) {
       InstanceConstant instance = constant;
       return isLoweredMapConstant(
-          instance.fieldValues[unmodifiableSetMap.getterReference]);
+          instance.fieldValues[unmodifiableSetMap.getterReference]!);
     }
     return false;
   }
@@ -110,9 +107,9 @@
   void forEachLoweredSetConstantElement(
       Constant constant, void Function(Constant element) f) {
     assert(isLoweredSetConstant(constant));
-    final InstanceConstant instance = constant;
+    final InstanceConstant instance = constant as InstanceConstant;
     final Constant mapConstant =
-        instance.fieldValues[unmodifiableSetMap.getterReference];
+        instance.fieldValues[unmodifiableSetMap.getterReference]!;
     forEachLoweredMapConstantEntry(mapConstant, (Constant key, Constant value) {
       f(key);
     });
diff --git a/pkg/vm/lib/target/vm.dart b/pkg/vm/lib/target/vm.dart
index 7fbf03c..d80ff52 100644
--- a/pkg/vm/lib/target/vm.dart
+++ b/pkg/vm/lib/target/vm.dart
@@ -196,10 +196,15 @@
 
   @override
   void performTransformationsOnProcedure(
-      CoreTypes coreTypes, ClassHierarchy hierarchy, Procedure procedure,
+      CoreTypes coreTypes,
+      ClassHierarchy hierarchy,
+      Procedure procedure,
+      Map<String, String> environmentDefines,
       {void logger(String msg)}) {
+    bool productMode = environmentDefines["dart.vm.product"] == "true";
     transformAsync.transformProcedure(
-        new TypeEnvironment(coreTypes, hierarchy), procedure);
+        new TypeEnvironment(coreTypes, hierarchy), procedure,
+        productMode: productMode);
     logger?.call("Transformed async functions");
 
     lowering.transformProcedure(
diff --git a/tools/VERSION b/tools/VERSION
index 907806b..b5dfc3a 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 13
 PATCH 0
-PRERELEASE 213
+PRERELEASE 214
 PRERELEASE_PATCH 0
\ No newline at end of file