Version 2.14.0-220.0.dev

Merge commit '83e024269635dd86bef346eb590580a53ebcd3cf' into 'dev'
diff --git a/.dart_tool/package_config.json b/.dart_tool/package_config.json
index baa2b14..44baa91 100644
--- a/.dart_tool/package_config.json
+++ b/.dart_tool/package_config.json
@@ -11,7 +11,7 @@
     "constraint, update this by running tools/generate_package_config.dart."
   ],
   "configVersion": 2,
-  "generated": "2021-06-07T10:25:46.125579",
+  "generated": "2021-06-15T06:05:26.285605",
   "generator": "tools/generate_package_config.dart",
   "packages": [
     {
@@ -82,7 +82,7 @@
       "name": "analyzer",
       "rootUri": "../pkg/analyzer",
       "packageUri": "lib/",
-      "languageVersion": "2.12"
+      "languageVersion": "2.13"
     },
     {
       "name": "analyzer_cli",
diff --git a/pkg/_fe_analyzer_shared/lib/src/parser/forwarding_listener.dart b/pkg/_fe_analyzer_shared/lib/src/parser/forwarding_listener.dart
index 3f29b84..da87016 100644
--- a/pkg/_fe_analyzer_shared/lib/src/parser/forwarding_listener.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/parser/forwarding_listener.dart
@@ -649,9 +649,10 @@
   }
 
   @override
-  void endExtensionDeclaration(
-      Token extensionKeyword, Token onKeyword, Token endToken) {
-    listener?.endExtensionDeclaration(extensionKeyword, onKeyword, endToken);
+  void endExtensionDeclaration(Token extensionKeyword, Token? typeKeyword,
+      Token onKeyword, Token endToken) {
+    listener?.endExtensionDeclaration(
+        extensionKeyword, typeKeyword, onKeyword, endToken);
   }
 
   @override
diff --git a/pkg/_fe_analyzer_shared/lib/src/parser/listener.dart b/pkg/_fe_analyzer_shared/lib/src/parser/listener.dart
index 4b1f39c..2c8e578 100644
--- a/pkg/_fe_analyzer_shared/lib/src/parser/listener.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/parser/listener.dart
@@ -235,8 +235,8 @@
   /// - substructures from [beginExtensionDeclaration]
   /// - on type
   /// - body
-  void endExtensionDeclaration(
-      Token extensionKeyword, Token onKeyword, Token endToken) {
+  void endExtensionDeclaration(Token extensionKeyword, Token? typeKeyword,
+      Token onKeyword, Token endToken) {
     logEvent('ExtensionDeclaration');
   }
 
diff --git a/pkg/_fe_analyzer_shared/lib/src/parser/parser_impl.dart b/pkg/_fe_analyzer_shared/lib/src/parser/parser_impl.dart
index 64c5be3..3992b34 100644
--- a/pkg/_fe_analyzer_shared/lib/src/parser/parser_impl.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/parser/parser_impl.dart
@@ -2311,6 +2311,15 @@
     Token token = extensionKeyword;
     listener.beginExtensionDeclarationPrelude(extensionKeyword);
     Token? name = token.next!;
+    Token? typeKeyword = null;
+    if (name.isIdentifier &&
+        name.lexeme == 'type' &&
+        name.next!.isIdentifier &&
+        !optional('on', name.next!)) {
+      typeKeyword = name;
+      token = token.next!;
+      name = token.next!;
+    }
     if (name.isIdentifier && !optional('on', name)) {
       token = name;
       if (name.type.isBuiltIn) {
@@ -2365,7 +2374,8 @@
     }
     token = parseClassOrMixinOrExtensionBody(
         token, DeclarationKind.Extension, name?.lexeme);
-    listener.endExtensionDeclaration(extensionKeyword, onKeyword, token);
+    listener.endExtensionDeclaration(
+        extensionKeyword, typeKeyword, onKeyword, token);
     return token;
   }
 
diff --git a/pkg/analysis_server/test/lsp/code_actions_refactor_test.dart b/pkg/analysis_server/test/lsp/code_actions_refactor_test.dart
index 791c627d..6891326 100644
--- a/pkg/analysis_server/test/lsp/code_actions_refactor_test.dart
+++ b/pkg/analysis_server/test/lsp/code_actions_refactor_test.dart
@@ -27,25 +27,32 @@
 class ExtractMethodRefactorCodeActionsTest extends AbstractCodeActionsTest {
   final extractMethodTitle = 'Extract Method';
 
-  /// A stream of strings (CREATE, BEGIN, END) corresponding to progress requests
-  /// and notifications for convenience in testing.
+  /// A stream of strings (CREATE, BEGIN, END) corresponding to progress
+  /// requests and notifications for convenience in testing.
+  ///
+  /// Analyzing statuses are not included.
   Stream<String> get progressUpdates {
     final controller = StreamController<String>();
 
     requestsFromServer
         .where((r) => r.method == Method.window_workDoneProgress_create)
         .listen((request) async {
-      controller.add('CREATE');
+      final params = WorkDoneProgressCreateParams.fromJson(request.params);
+      if (params.token != analyzingProgressToken) {
+        controller.add('CREATE');
+      }
     }, onDone: controller.close);
     notificationsFromServer
         .where((n) => n.method == Method.progress)
         .listen((notification) {
       final params = ProgressParams.fromJson(notification.params);
-      if (WorkDoneProgressBegin.canParse(params.value, nullLspJsonReporter)) {
-        controller.add('BEGIN');
-      } else if (WorkDoneProgressEnd.canParse(
-          params.value, nullLspJsonReporter)) {
-        controller.add('END');
+      if (params.token != analyzingProgressToken) {
+        if (WorkDoneProgressBegin.canParse(params.value, nullLspJsonReporter)) {
+          controller.add('BEGIN');
+        } else if (WorkDoneProgressEnd.canParse(
+            params.value, nullLspJsonReporter)) {
+          controller.add('END');
+        }
       }
     });
 
@@ -323,35 +330,16 @@
         windowCapabilities:
             withWorkDoneProgressSupport(emptyWindowClientCapabilities));
 
-    // Capture progress-related messages in a list in the order they arrive.
-    final progressRequests = <String>[];
-    requestsFromServer
-        .where((r) => r.method == Method.window_workDoneProgress_create)
-        .listen((request) async {
-      progressRequests.add('CREATE');
-    });
-    notificationsFromServer
-        .where((n) => n.method == Method.progress)
-        .listen((notification) {
-      final params = ProgressParams.fromJson(notification.params);
-      if (WorkDoneProgressBegin.canParse(params.value, nullLspJsonReporter)) {
-        progressRequests.add('BEGIN');
-      } else if (WorkDoneProgressEnd.canParse(
-          params.value, nullLspJsonReporter)) {
-        progressRequests.add('END');
-      }
-    });
-
     final codeActions = await getCodeActions(mainFileUri.toString(),
         range: rangeFromMarkers(content));
     final codeAction =
         findCommand(codeActions, Commands.performRefactor, extractMethodTitle)!;
 
+    // Ensure the progress messages come through and in the correct order.
+    expect(progressUpdates, emitsInOrder(['CREATE', 'BEGIN', 'END']));
+
     await verifyCodeActionEdits(
         codeAction, withoutMarkers(content), expectedContent);
-
-    // Ensure the progress messages came through and in the correct order.
-    expect(progressRequests, equals(['CREATE', 'BEGIN', 'END']));
   }
 }
 
diff --git a/pkg/analyzer/lib/dart/ast/ast.dart b/pkg/analyzer/lib/dart/ast/ast.dart
index e415663..0079659 100644
--- a/pkg/analyzer/lib/dart/ast/ast.dart
+++ b/pkg/analyzer/lib/dart/ast/ast.dart
@@ -2334,6 +2334,9 @@
   /// Return the right curly bracket.
   Token get rightBracket;
 
+  /// Return the token representing the 'type' keyword.
+  Token? get typeKeyword;
+
   /// Return the type parameters for the extension, or `null` if the extension
   /// does not have any type parameters.
   TypeParameterList? get typeParameters;
diff --git a/pkg/analyzer/lib/dart/ast/ast_factory.dart b/pkg/analyzer/lib/dart/ast/ast_factory.dart
index bf82edf..15022fc 100644
--- a/pkg/analyzer/lib/dart/ast/ast_factory.dart
+++ b/pkg/analyzer/lib/dart/ast/ast_factory.dart
@@ -339,7 +339,8 @@
       {Comment? comment,
       List<Annotation>? metadata,
       required Token extensionKeyword,
-      required SimpleIdentifier? name,
+      Token? typeKeyword,
+      SimpleIdentifier? name,
       TypeParameterList? typeParameters,
       required Token onKeyword,
       required TypeAnnotation extendedType,
diff --git a/pkg/analyzer/lib/src/dart/ast/ast.dart b/pkg/analyzer/lib/src/dart/ast/ast.dart
index 53393a1..6d16a45 100644
--- a/pkg/analyzer/lib/src/dart/ast/ast.dart
+++ b/pkg/analyzer/lib/src/dart/ast/ast.dart
@@ -3702,6 +3702,9 @@
   @override
   Token extensionKeyword;
 
+  @override
+  Token? typeKeyword;
+
   /// The name of the extension, or `null` if the extension does not have a
   /// name.
   SimpleIdentifierImpl? _name;
@@ -3731,6 +3734,7 @@
       CommentImpl? comment,
       List<Annotation>? metadata,
       this.extensionKeyword,
+      this.typeKeyword,
       this._name,
       this._typeParameters,
       this.onKeyword,
diff --git a/pkg/analyzer/lib/src/dart/ast/ast_factory.dart b/pkg/analyzer/lib/src/dart/ast/ast_factory.dart
index 6f98990..029702e 100644
--- a/pkg/analyzer/lib/src/dart/ast/ast_factory.dart
+++ b/pkg/analyzer/lib/src/dart/ast/ast_factory.dart
@@ -430,7 +430,8 @@
           {Comment? comment,
           List<Annotation>? metadata,
           required Token extensionKeyword,
-          required SimpleIdentifier? name,
+          Token? typeKeyword,
+          SimpleIdentifier? name,
           TypeParameterList? typeParameters,
           required Token onKeyword,
           required TypeAnnotation extendedType,
@@ -441,6 +442,7 @@
           comment as CommentImpl?,
           metadata,
           extensionKeyword,
+          typeKeyword,
           name as SimpleIdentifierImpl?,
           typeParameters as TypeParameterListImpl?,
           onKeyword,
diff --git a/pkg/analyzer/lib/src/dart/ast/to_source_visitor.dart b/pkg/analyzer/lib/src/dart/ast/to_source_visitor.dart
index 61cbd65..bbec62f 100644
--- a/pkg/analyzer/lib/src/dart/ast/to_source_visitor.dart
+++ b/pkg/analyzer/lib/src/dart/ast/to_source_visitor.dart
@@ -466,6 +466,7 @@
   void visitExtensionDeclaration(ExtensionDeclaration node) {
     safelyVisitNodeListWithSeparatorAndSuffix(node.metadata, ' ', ' ');
     safelyVisitTokenWithSuffix(node.extensionKeyword, ' ');
+    safelyVisitTokenWithSuffix(node.typeKeyword, ' ');
     safelyVisitNode(node.name);
     safelyVisitNode(node.typeParameters);
     sink.write(' ');
diff --git a/pkg/analyzer/lib/src/dart/resolver/annotation_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/annotation_resolver.dart
index 98832a4..ca6b0c4 100644
--- a/pkg/analyzer/lib/src/dart/resolver/annotation_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/annotation_resolver.dart
@@ -2,7 +2,6 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-import 'package:_fe_analyzer_shared/src/flow_analysis/flow_analysis.dart';
 import 'package:analyzer/dart/analysis/features.dart';
 import 'package:analyzer/dart/ast/ast.dart';
 import 'package:analyzer/dart/element/element.dart';
@@ -29,8 +28,8 @@
   bool get _genericMetadataIsEnabled =>
       _definingLibrary.featureSet.isEnabled(Feature.generic_metadata);
 
-  void resolve(AnnotationImpl node,
-      List<Map<DartType, NonPromotionReason> Function()> whyNotPromotedList) {
+  void resolve(
+      AnnotationImpl node, List<WhyNotPromotedGetter> whyNotPromotedList) {
     node.typeArguments?.accept(_resolver);
     _resolve(node, whyNotPromotedList);
   }
@@ -40,7 +39,7 @@
     ClassElement classElement,
     SimpleIdentifierImpl? constructorName,
     ArgumentList argumentList,
-    List<Map<DartType, NonPromotionReason> Function()> whyNotPromotedList,
+    List<WhyNotPromotedGetter> whyNotPromotedList,
   ) {
     ConstructorElement? constructorElement;
     if (constructorName != null) {
@@ -72,7 +71,7 @@
     AnnotationImpl node,
     ClassElement classElement,
     SimpleIdentifierImpl? getterName,
-    List<Map<DartType, NonPromotionReason> Function()> whyNotPromotedList,
+    List<WhyNotPromotedGetter> whyNotPromotedList,
   ) {
     ExecutableElement? getter;
     if (getterName != null) {
@@ -108,7 +107,7 @@
     ConstructorElement? constructorElement,
     ArgumentList argumentList,
     InterfaceType Function(List<DartType> typeArguments) instantiateElement,
-    List<Map<DartType, NonPromotionReason> Function()> whyNotPromotedList,
+    List<WhyNotPromotedGetter> whyNotPromotedList,
   ) {
     constructorElement = _resolver.toLegacyElement(constructorElement);
     constructorName?.staticElement = constructorElement;
@@ -240,7 +239,7 @@
     AnnotationImpl node,
     ExtensionElement extensionElement,
     SimpleIdentifierImpl? getterName,
-    List<Map<DartType, NonPromotionReason> Function()> whyNotPromotedList,
+    List<WhyNotPromotedGetter> whyNotPromotedList,
   ) {
     ExecutableElement? getter;
     if (getterName != null) {
@@ -268,7 +267,7 @@
     AnnotationImpl node,
     SimpleIdentifierImpl name,
     PropertyAccessorElement element,
-    List<Map<DartType, NonPromotionReason> Function()> whyNotPromotedList,
+    List<WhyNotPromotedGetter> whyNotPromotedList,
   ) {
     element = _resolver.toLegacyElement(element);
     name.staticElement = element;
@@ -278,8 +277,8 @@
     _visitArguments(node, whyNotPromotedList);
   }
 
-  void _resolve(AnnotationImpl node,
-      List<Map<DartType, NonPromotionReason> Function()> whyNotPromotedList) {
+  void _resolve(
+      AnnotationImpl node, List<WhyNotPromotedGetter> whyNotPromotedList) {
     SimpleIdentifierImpl name1;
     SimpleIdentifierImpl? name2;
     SimpleIdentifierImpl? name3;
@@ -471,7 +470,7 @@
     SimpleIdentifierImpl? constructorName,
     InterfaceType aliasedType,
     ArgumentList argumentList,
-    List<Map<DartType, NonPromotionReason> Function()> whyNotPromotedList,
+    List<WhyNotPromotedGetter> whyNotPromotedList,
   ) {
     var constructorElement = aliasedType.lookUpConstructor(
       constructorName?.name,
@@ -499,7 +498,7 @@
     AnnotationImpl node,
     TypeAliasElement typeAliasElement,
     SimpleIdentifierImpl? getterName,
-    List<Map<DartType, NonPromotionReason> Function()> whyNotPromotedList,
+    List<WhyNotPromotedGetter> whyNotPromotedList,
   ) {
     ExecutableElement? getter;
     var aliasedType = typeAliasElement.aliasedType;
@@ -527,8 +526,8 @@
     _visitArguments(node, whyNotPromotedList);
   }
 
-  void _visitArguments(AnnotationImpl node,
-      List<Map<DartType, NonPromotionReason> Function()> whyNotPromotedList) {
+  void _visitArguments(
+      AnnotationImpl node, List<WhyNotPromotedGetter> whyNotPromotedList) {
     var arguments = node.arguments;
     if (arguments != null) {
       _resolver.visitArgumentList(arguments,
diff --git a/pkg/analyzer/lib/src/dart/resolver/extension_member_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/extension_member_resolver.dart
index ad735aa..6ad246d 100644
--- a/pkg/analyzer/lib/src/dart/resolver/extension_member_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/extension_member_resolver.dart
@@ -2,7 +2,6 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-import 'package:_fe_analyzer_shared/src/flow_analysis/flow_analysis.dart';
 import 'package:analyzer/dart/analysis/features.dart';
 import 'package:analyzer/dart/ast/ast.dart';
 import 'package:analyzer/dart/ast/syntactic_entity.dart';
@@ -126,8 +125,8 @@
   }
 
   /// Perform upward inference for the override.
-  void resolveOverride(ExtensionOverride node,
-      List<Map<DartType, NonPromotionReason> Function()> whyNotPromotedList) {
+  void resolveOverride(
+      ExtensionOverride node, List<WhyNotPromotedGetter> whyNotPromotedList) {
     var nodeImpl = node as ExtensionOverrideImpl;
     var element = node.staticElement!;
     var typeParameters = element.typeParameters;
diff --git a/pkg/analyzer/lib/src/dart/resolver/function_expression_invocation_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/function_expression_invocation_resolver.dart
index 199ea71..8543e5e 100644
--- a/pkg/analyzer/lib/src/dart/resolver/function_expression_invocation_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/function_expression_invocation_resolver.dart
@@ -2,7 +2,6 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-import 'package:_fe_analyzer_shared/src/flow_analysis/flow_analysis.dart';
 import 'package:analyzer/dart/ast/ast.dart';
 import 'package:analyzer/dart/element/element.dart';
 import 'package:analyzer/dart/element/type.dart';
@@ -37,7 +36,7 @@
       _resolver.nullableDereferenceVerifier;
 
   void resolve(FunctionExpressionInvocationImpl node,
-      List<Map<DartType, NonPromotionReason> Function()> whyNotPromotedList) {
+      List<WhyNotPromotedGetter> whyNotPromotedList) {
     var function = node.function;
 
     if (function is ExtensionOverrideImpl) {
@@ -104,7 +103,7 @@
   }
 
   void _resolve(FunctionExpressionInvocationImpl node, FunctionType rawType,
-      List<Map<DartType, NonPromotionReason> Function()> whyNotPromotedList) {
+      List<WhyNotPromotedGetter> whyNotPromotedList) {
     _inferenceHelper.resolveFunctionExpressionInvocation(
       node: node,
       rawType: rawType,
@@ -118,7 +117,7 @@
   }
 
   void _resolveArguments(FunctionExpressionInvocationImpl node,
-      List<Map<DartType, NonPromotionReason> Function()> whyNotPromotedList) {
+      List<WhyNotPromotedGetter> whyNotPromotedList) {
     _resolver.visitArgumentList(node.argumentList,
         whyNotPromotedList: whyNotPromotedList);
   }
@@ -126,7 +125,7 @@
   void _resolveReceiverExtensionOverride(
     FunctionExpressionInvocationImpl node,
     ExtensionOverride function,
-    List<Map<DartType, NonPromotionReason> Function()> whyNotPromotedList,
+    List<WhyNotPromotedGetter> whyNotPromotedList,
   ) {
     var result = _extensionResolver.getOverrideMember(
       function,
@@ -159,7 +158,7 @@
     FunctionExpressionInvocationImpl node,
     Expression function,
     InterfaceType receiverType,
-    List<Map<DartType, NonPromotionReason> Function()> whyNotPromotedList,
+    List<WhyNotPromotedGetter> whyNotPromotedList,
   ) {
     var result = _typePropertyResolver.resolve(
       receiver: function,
@@ -196,7 +195,7 @@
   }
 
   void _unresolved(FunctionExpressionInvocationImpl node, DartType type,
-      List<Map<DartType, NonPromotionReason> Function()> whyNotPromotedList) {
+      List<WhyNotPromotedGetter> whyNotPromotedList) {
     _setExplicitTypeArgumentTypes(node);
     _resolveArguments(node, whyNotPromotedList);
     node.staticInvokeType = DynamicTypeImpl.instance;
diff --git a/pkg/analyzer/lib/src/dart/resolver/invocation_inference_helper.dart b/pkg/analyzer/lib/src/dart/resolver/invocation_inference_helper.dart
index 63b77b3..1bc11bc 100644
--- a/pkg/analyzer/lib/src/dart/resolver/invocation_inference_helper.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/invocation_inference_helper.dart
@@ -2,7 +2,6 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-import 'package:_fe_analyzer_shared/src/flow_analysis/flow_analysis.dart';
 import 'package:analyzer/dart/analysis/features.dart';
 import 'package:analyzer/dart/ast/ast.dart';
 import 'package:analyzer/dart/element/element.dart';
@@ -295,8 +294,7 @@
   void resolveFunctionExpressionInvocation({
     required FunctionExpressionInvocationImpl node,
     required FunctionType rawType,
-    required List<Map<DartType, NonPromotionReason> Function()>
-        whyNotPromotedList,
+    required List<WhyNotPromotedGetter> whyNotPromotedList,
   }) {
     _resolveInvocation(
       rawType: rawType,
@@ -320,8 +318,7 @@
   void resolveMethodInvocation({
     required MethodInvocationImpl node,
     required FunctionType rawType,
-    required List<Map<DartType, NonPromotionReason> Function()>
-        whyNotPromotedList,
+    required List<WhyNotPromotedGetter> whyNotPromotedList,
   }) {
     _resolveInvocation(
       rawType: rawType,
@@ -420,7 +417,7 @@
   }
 
   void _resolveArguments(ArgumentList argumentList,
-      List<Map<DartType, NonPromotionReason> Function()> whyNotPromotedList) {
+      List<WhyNotPromotedGetter> whyNotPromotedList) {
     _resolver.visitArgumentList(argumentList,
         isIdentical: _isCallToIdentical(argumentList.parent),
         whyNotPromotedList: whyNotPromotedList);
@@ -433,8 +430,7 @@
     required ArgumentListImpl argumentList,
     required bool isConst,
     required AstNode errorNode,
-    required List<Map<DartType, NonPromotionReason> Function()>
-        whyNotPromotedList,
+    required List<WhyNotPromotedGetter> whyNotPromotedList,
   }) {
     if (typeArgumentList != null) {
       _resolveInvocationWithTypeArguments(
@@ -462,8 +458,7 @@
     required ArgumentList argumentList,
     required bool isConst,
     required AstNode errorNode,
-    required List<Map<DartType, NonPromotionReason> Function()>
-        whyNotPromotedList,
+    required List<WhyNotPromotedGetter> whyNotPromotedList,
   }) {
     var typeParameters = rawType.typeFormals;
 
@@ -503,8 +498,7 @@
     required FunctionType rawType,
     required TypeArgumentList typeArgumentList,
     required ArgumentList argumentList,
-    required List<Map<DartType, NonPromotionReason> Function()>
-        whyNotPromotedList,
+    required List<WhyNotPromotedGetter> whyNotPromotedList,
   }) {
     var typeParameters = rawType.typeFormals;
 
diff --git a/pkg/analyzer/lib/src/dart/resolver/method_invocation_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/method_invocation_resolver.dart
index ff75f88..7485e6e 100644
--- a/pkg/analyzer/lib/src/dart/resolver/method_invocation_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/method_invocation_resolver.dart
@@ -2,7 +2,6 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-import 'package:_fe_analyzer_shared/src/flow_analysis/flow_analysis.dart';
 import 'package:analyzer/dart/ast/ast.dart';
 import 'package:analyzer/dart/element/element.dart';
 import 'package:analyzer/dart/element/scope.dart';
@@ -82,7 +81,7 @@
   TypeSystemImpl get _typeSystem => _resolver.typeSystem;
 
   void resolve(MethodInvocationImpl node,
-      List<Map<DartType, NonPromotionReason> Function()> whyNotPromotedList) {
+      List<WhyNotPromotedGetter> whyNotPromotedList) {
     _invocation = node;
 
     var nameNode = node.methodName;
@@ -235,7 +234,7 @@
   }
 
   void _reportInvocationOfNonFunction(MethodInvocationImpl node,
-      List<Map<DartType, NonPromotionReason> Function()> whyNotPromotedList) {
+      List<WhyNotPromotedGetter> whyNotPromotedList) {
     _setDynamicResolution(node,
         setNameTypeToDynamic: false, whyNotPromotedList: whyNotPromotedList);
     _resolver.errorReporter.reportErrorForNode(
@@ -268,8 +267,7 @@
     MethodInvocationImpl node, {
     required String? prefix,
     required String name,
-    required List<Map<DartType, NonPromotionReason> Function()>
-        whyNotPromotedList,
+    required List<WhyNotPromotedGetter> whyNotPromotedList,
   }) {
     _setDynamicResolution(node, whyNotPromotedList: whyNotPromotedList);
 
@@ -288,7 +286,7 @@
       MethodInvocationImpl node,
       String name,
       ClassElement typeReference,
-      List<Map<DartType, NonPromotionReason> Function()> whyNotPromotedList) {
+      List<WhyNotPromotedGetter> whyNotPromotedList) {
     _setDynamicResolution(node, whyNotPromotedList: whyNotPromotedList);
     _resolver.errorReporter.reportErrorForNode(
       CompileTimeErrorCode.UNDEFINED_METHOD,
@@ -298,7 +296,7 @@
   }
 
   void _reportUseOfVoidType(MethodInvocationImpl node, AstNode errorNode,
-      List<Map<DartType, NonPromotionReason> Function()> whyNotPromotedList) {
+      List<WhyNotPromotedGetter> whyNotPromotedList) {
     _setDynamicResolution(node, whyNotPromotedList: whyNotPromotedList);
     _resolver.errorReporter.reportErrorForNode(
       CompileTimeErrorCode.USE_OF_VOID_RESULT,
@@ -309,7 +307,7 @@
   /// [InvocationExpression.staticInvokeType] has been set for the [node].
   /// Use it to set context for arguments, and resolve them.
   void _resolveArguments(MethodInvocationImpl node,
-      List<Map<DartType, NonPromotionReason> Function()> whyNotPromotedList) {
+      List<WhyNotPromotedGetter> whyNotPromotedList) {
     // TODO(scheglov) This is bad, don't write raw type, carry it
     _inferenceHelper.inferArgumentTypesForInvocation(
       node,
@@ -320,7 +318,7 @@
   }
 
   void _resolveArguments_finishInference(MethodInvocationImpl node,
-      List<Map<DartType, NonPromotionReason> Function()> whyNotPromotedList) {
+      List<WhyNotPromotedGetter> whyNotPromotedList) {
     _resolveArguments(node, whyNotPromotedList);
 
     // TODO(scheglov) This is bad, don't put / get raw FunctionType this way.
@@ -359,7 +357,7 @@
       ExtensionElement extension,
       SimpleIdentifierImpl nameNode,
       String name,
-      List<Map<DartType, NonPromotionReason> Function()> whyNotPromotedList) {
+      List<WhyNotPromotedGetter> whyNotPromotedList) {
     var getter = extension.getGetter(name);
     if (getter != null) {
       getter = _resolver.toLegacyElement(getter);
@@ -391,7 +389,7 @@
       ExtensionOverride override,
       SimpleIdentifierImpl nameNode,
       String name,
-      List<Map<DartType, NonPromotionReason> Function()> whyNotPromotedList) {
+      List<WhyNotPromotedGetter> whyNotPromotedList) {
     var result = _extensionResolver.getOverrideMember(override, name);
     var member = _resolver.toLegacyElement(result.getter);
 
@@ -430,7 +428,7 @@
   }
 
   void _resolveReceiverDynamicBounded(MethodInvocationImpl node,
-      List<Map<DartType, NonPromotionReason> Function()> whyNotPromotedList) {
+      List<WhyNotPromotedGetter> whyNotPromotedList) {
     var nameNode = node.methodName;
 
     var objectElement = _typeSystem.typeProvider.objectElement;
@@ -466,7 +464,7 @@
     DartType receiverType,
     SimpleIdentifierImpl nameNode,
     String name,
-    List<Map<DartType, NonPromotionReason> Function()> whyNotPromotedList,
+    List<WhyNotPromotedGetter> whyNotPromotedList,
   ) {
     if (name == FunctionElement.CALL_METHOD_NAME) {
       _setResolution(node, receiverType, whyNotPromotedList);
@@ -492,7 +490,7 @@
     MethodInvocationImpl node,
     Expression receiver,
     DartType receiverType,
-    List<Map<DartType, NonPromotionReason> Function()> whyNotPromotedList,
+    List<WhyNotPromotedGetter> whyNotPromotedList,
   ) {
     _setExplicitTypeArgumentTypes();
 
@@ -545,7 +543,7 @@
       MethodInvocationImpl node,
       SimpleIdentifierImpl nameNode,
       String name,
-      List<Map<DartType, NonPromotionReason> Function()> whyNotPromotedList) {
+      List<WhyNotPromotedGetter> whyNotPromotedList) {
     var element = nameScope.lookup(name).getter;
     if (element != null) {
       element = _resolver.toLegacyElement(element);
@@ -604,7 +602,7 @@
       PrefixElement prefix,
       SimpleIdentifierImpl nameNode,
       String name,
-      List<Map<DartType, NonPromotionReason> Function()> whyNotPromotedList) {
+      List<WhyNotPromotedGetter> whyNotPromotedList) {
     // Note: prefix?.bar is reported as an error in ElementResolver.
 
     if (name == FunctionElement.LOAD_LIBRARY_NAME) {
@@ -651,7 +649,7 @@
       SuperExpression receiver,
       SimpleIdentifierImpl nameNode,
       String name,
-      List<Map<DartType, NonPromotionReason> Function()> whyNotPromotedList) {
+      List<WhyNotPromotedGetter> whyNotPromotedList) {
     var enclosingClass = _resolver.enclosingClass;
     if (SuperContext.of(receiver) != SuperContext.valid) {
       _setDynamicResolution(node, whyNotPromotedList: whyNotPromotedList);
@@ -705,8 +703,7 @@
     required SimpleIdentifierImpl nameNode,
     required String name,
     required Expression receiverErrorNode,
-    required List<Map<DartType, NonPromotionReason> Function()>
-        whyNotPromotedList,
+    required List<WhyNotPromotedGetter> whyNotPromotedList,
   }) {
     var result = _resolver.typePropertyResolver.resolve(
       receiver: receiver,
@@ -761,7 +758,7 @@
       ClassElement receiver,
       SimpleIdentifierImpl nameNode,
       String name,
-      List<Map<DartType, NonPromotionReason> Function()> whyNotPromotedList) {
+      List<WhyNotPromotedGetter> whyNotPromotedList) {
     if (node.isCascaded) {
       receiver = _typeType.element;
     }
@@ -847,8 +844,7 @@
 
   void _setDynamicResolution(MethodInvocationImpl node,
       {bool setNameTypeToDynamic = true,
-      required List<Map<DartType, NonPromotionReason> Function()>
-          whyNotPromotedList}) {
+      required List<WhyNotPromotedGetter> whyNotPromotedList}) {
     if (setNameTypeToDynamic) {
       node.methodName.staticType = _dynamicType;
     }
@@ -875,7 +871,7 @@
   }
 
   void _setResolution(MethodInvocationImpl node, DartType type,
-      List<Map<DartType, NonPromotionReason> Function()> whyNotPromotedList) {
+      List<WhyNotPromotedGetter> whyNotPromotedList) {
     // TODO(scheglov) We need this for StaticTypeAnalyzer to run inference.
     // But it seems weird. Do we need to know the raw type of a function?!
     node.methodName.staticType = type;
diff --git a/pkg/analyzer/lib/src/fasta/ast_builder.dart b/pkg/analyzer/lib/src/fasta/ast_builder.dart
index 5bdd4c2..88a50ee 100644
--- a/pkg/analyzer/lib/src/fasta/ast_builder.dart
+++ b/pkg/analyzer/lib/src/fasta/ast_builder.dart
@@ -246,6 +246,7 @@
       comment: comment,
       metadata: metadata,
       extensionKeyword: extensionKeyword,
+      typeKeyword: null,
       name: name,
       typeParameters: typeParameters,
       onKeyword: Tokens.on_(),
@@ -1186,12 +1187,23 @@
   }
 
   @override
-  void endExtensionDeclaration(
-      Token extensionKeyword, Token onKeyword, Token token) {
+  void endExtensionDeclaration(Token extensionKeyword, Token? typeKeyword,
+      Token onKeyword, Token token) {
+    if (typeKeyword != null && !enableConstructorTearoffs) {
+      var feature = ExperimentalFeatures.constructor_tearoffs;
+      handleRecoverableError(
+          templateExperimentNotEnabled.withArguments(
+            feature.enableString,
+            _versionAsString(ExperimentStatus.currentVersion),
+          ),
+          typeKeyword,
+          typeKeyword);
+    }
     var type = pop() as TypeAnnotation;
     extensionDeclaration!
       ..extendedType = type
-      ..onKeyword = onKeyword;
+      ..onKeyword = onKeyword
+      ..typeKeyword = typeKeyword;
     extensionDeclaration = null;
   }
 
diff --git a/pkg/analyzer/lib/src/generated/element_resolver.dart b/pkg/analyzer/lib/src/generated/element_resolver.dart
index 7679722..69bdf31 100644
--- a/pkg/analyzer/lib/src/generated/element_resolver.dart
+++ b/pkg/analyzer/lib/src/generated/element_resolver.dart
@@ -2,7 +2,6 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-import 'package:_fe_analyzer_shared/src/flow_analysis/flow_analysis.dart';
 import 'package:analyzer/dart/ast/ast.dart';
 import 'package:analyzer/dart/ast/visitor.dart';
 import 'package:analyzer/dart/element/element.dart';
@@ -390,8 +389,7 @@
 
   @override
   void visitMethodInvocation(MethodInvocation node,
-      {List<Map<DartType, NonPromotionReason> Function()>?
-          whyNotPromotedList}) {
+      {List<WhyNotPromotedGetter>? whyNotPromotedList}) {
     whyNotPromotedList ??= [];
     _methodInvocationResolver.resolve(
         node as MethodInvocationImpl, whyNotPromotedList);
diff --git a/pkg/analyzer/lib/src/generated/resolver.dart b/pkg/analyzer/lib/src/generated/resolver.dart
index 72b1e6a..88b7c38 100644
--- a/pkg/analyzer/lib/src/generated/resolver.dart
+++ b/pkg/analyzer/lib/src/generated/resolver.dart
@@ -67,6 +67,10 @@
 import 'package:analyzer/src/util/ast_data_extractor.dart';
 import 'package:meta/meta.dart';
 
+/// A function which returns [NonPromotionReason]s that various types are not
+/// promoted.
+typedef WhyNotPromotedGetter = Map<DartType, NonPromotionReason> Function();
+
 /// Maintains and manages contextual type information used for
 /// inferring types.
 class InferenceContext {
@@ -422,7 +426,7 @@
   ///
   /// See [StaticWarningCode.ARGUMENT_TYPE_NOT_ASSIGNABLE].
   void checkForArgumentTypesNotAssignableInList(ArgumentList argumentList,
-      List<Map<DartType, NonPromotionReason> Function()> whyNotPromotedList) {
+      List<WhyNotPromotedGetter> whyNotPromotedList) {
     var arguments = argumentList.arguments;
     for (int i = 0; i < arguments.length; i++) {
       checkForArgumentTypeNotAssignableForArgument(arguments[i],
@@ -893,7 +897,7 @@
   @override
   void visitArgumentList(ArgumentList node,
       {bool isIdentical = false,
-      List<Map<DartType, NonPromotionReason> Function()>? whyNotPromotedList}) {
+      List<WhyNotPromotedGetter>? whyNotPromotedList}) {
     whyNotPromotedList ??= [];
     var callerType = InferenceContext.getContext(node);
     NodeList<Expression> arguments = node.arguments;
@@ -2235,7 +2239,7 @@
   /// as for method invocations.
   void _resolveRewrittenFunctionExpressionInvocation(
     FunctionExpressionInvocation node,
-    List<Map<DartType, NonPromotionReason> Function()> whyNotPromotedList,
+    List<WhyNotPromotedGetter> whyNotPromotedList,
   ) {
     var function = node.function;
 
diff --git a/pkg/analyzer/lib/src/generated/testing/ast_test_factory.dart b/pkg/analyzer/lib/src/generated/testing/ast_test_factory.dart
index 39ef023..70d878c 100644
--- a/pkg/analyzer/lib/src/generated/testing/ast_test_factory.dart
+++ b/pkg/analyzer/lib/src/generated/testing/ast_test_factory.dart
@@ -500,6 +500,7 @@
 
   static ExtensionDeclarationImpl extensionDeclaration(
           {required String name,
+          required bool isExtensionTypeDeclaration,
           TypeParameterList? typeParameters,
           required TypeAnnotation extendedType,
           List<ClassMember> members = const []}) =>
@@ -507,6 +508,9 @@
           comment: null,
           metadata: null,
           extensionKeyword: TokenFactory.tokenFromKeyword(Keyword.EXTENSION),
+          typeKeyword: isExtensionTypeDeclaration
+              ? TokenFactory.tokenFromString('type')
+              : null,
           name: identifier3(name),
           typeParameters: typeParameters,
           onKeyword: TokenFactory.tokenFromKeyword(Keyword.ON),
diff --git a/pkg/analyzer/lib/src/summary2/ast_text_printer.dart b/pkg/analyzer/lib/src/summary2/ast_text_printer.dart
index 18d7617..52f9b94e 100644
--- a/pkg/analyzer/lib/src/summary2/ast_text_printer.dart
+++ b/pkg/analyzer/lib/src/summary2/ast_text_printer.dart
@@ -337,6 +337,7 @@
   void visitExtensionDeclaration(ExtensionDeclaration node) {
     _compilationUnitMember(node);
     _token(node.extensionKeyword);
+    _token(node.typeKeyword);
     node.name?.accept(this);
     node.typeParameters?.accept(this);
     _token(node.onKeyword);
diff --git a/pkg/analyzer/lib/src/summary2/element_builder.dart b/pkg/analyzer/lib/src/summary2/element_builder.dart
index 25c9b9a..9d3fa60 100644
--- a/pkg/analyzer/lib/src/summary2/element_builder.dart
+++ b/pkg/analyzer/lib/src/summary2/element_builder.dart
@@ -276,6 +276,7 @@
   ) {
     var enclosingRef = _enclosingContext.reference;
 
+    var metadata = _buildAnnotations(node.metadata);
     for (var variable in node.fields.variables) {
       var nameNode = variable.name as SimpleIdentifierImpl;
       var name = nameNode.name;
@@ -297,7 +298,7 @@
       element.isFinal = node.fields.isFinal;
       element.isLate = node.fields.isLate;
       element.isStatic = node.isStatic;
-      element.metadata = _buildAnnotations(node.metadata);
+      element.metadata = metadata;
       _setCodeRange(element, variable);
       _setDocumentation(element, node);
 
@@ -785,6 +786,7 @@
   ) {
     var enclosingRef = _enclosingContext.reference;
 
+    var metadata = _buildAnnotations(node.metadata);
     for (var variable in node.variables.variables) {
       var nameNode = variable.name as SimpleIdentifierImpl;
       var name = nameNode.name;
@@ -802,7 +804,7 @@
       element.isExternal = node.externalKeyword != null;
       element.isFinal = node.variables.isFinal;
       element.isLate = node.variables.isLate;
-      element.metadata = _buildAnnotations(node.metadata);
+      element.metadata = metadata;
       _setCodeRange(element, variable);
       _setDocumentation(element, node);
 
diff --git a/pkg/analyzer/pubspec.yaml b/pkg/analyzer/pubspec.yaml
index b3cbff8..529d76a 100644
--- a/pkg/analyzer/pubspec.yaml
+++ b/pkg/analyzer/pubspec.yaml
@@ -4,7 +4,7 @@
 homepage: https://github.com/dart-lang/sdk/tree/master/pkg/analyzer
 
 environment:
-  sdk: '>=2.12.0 <3.0.0'
+  sdk: '>=2.13.0 <3.0.0'
 
 dependencies:
   _fe_analyzer_shared: ^22.0.0
diff --git a/pkg/analyzer/test/generated/parser_fasta_listener.dart b/pkg/analyzer/test/generated/parser_fasta_listener.dart
index dd2f9b4..c2509ed 100644
--- a/pkg/analyzer/test/generated/parser_fasta_listener.dart
+++ b/pkg/analyzer/test/generated/parser_fasta_listener.dart
@@ -748,9 +748,10 @@
   }
 
   @override
-  void endExtensionDeclaration(
-      Token extensionKeyword, Token onKeyword, Token token) {
-    super.endExtensionDeclaration(extensionKeyword, onKeyword, token);
+  void endExtensionDeclaration(Token extensionKeyword, Token? typeKeyword,
+      Token onKeyword, Token token) {
+    super.endExtensionDeclaration(
+        extensionKeyword, typeKeyword, onKeyword, token);
     end('ExtensionDeclaration');
   }
 
diff --git a/pkg/analyzer/test/src/dart/analysis/driver_caching_test.dart b/pkg/analyzer/test/src/dart/analysis/driver_caching_test.dart
index 621c1a4..300536e 100644
--- a/pkg/analyzer/test/src/dart/analysis/driver_caching_test.dart
+++ b/pkg/analyzer/test/src/dart/analysis/driver_caching_test.dart
@@ -273,9 +273,10 @@
   /// But this method is used to check returning errors from the cache, or
   /// recomputing when the cache key is expected to be different.
   Future<List<AnalysisError>> _computeTestFileErrors() async {
-    var errorsResult = await contextFor(testFilePath)
+    var testFilePathConverted = convertPath(testFilePath);
+    var errorsResult = await contextFor(testFilePathConverted)
         .currentSession
-        .getErrors2(testFilePath) as ErrorsResult;
+        .getErrors2(testFilePathConverted) as ErrorsResult;
     return errorsResult.errors;
   }
 }
diff --git a/pkg/analyzer/test/src/dart/ast/to_source_visitor_test.dart b/pkg/analyzer/test/src/dart/ast/to_source_visitor_test.dart
index 9bff2bc..9a41bd0 100644
--- a/pkg/analyzer/test/src/dart/ast/to_source_visitor_test.dart
+++ b/pkg/analyzer/test/src/dart/ast/to_source_visitor_test.dart
@@ -830,7 +830,9 @@
     _assertSource(
         'extension E on C {}',
         AstTestFactory.extensionDeclaration(
-            name: 'E', extendedType: AstTestFactory.typeName4('C')));
+            name: 'E',
+            extendedType: AstTestFactory.typeName4('C'),
+            isExtensionTypeDeclaration: false));
   }
 
   void test_visitExtensionDeclaration_multipleMember() {
@@ -844,7 +846,8 @@
                   [AstTestFactory.variableDeclaration('a')]),
               AstTestFactory.fieldDeclaration2(
                   false, Keyword.VAR, [AstTestFactory.variableDeclaration('b')])
-            ]));
+            ],
+            isExtensionTypeDeclaration: false));
   }
 
   void test_visitExtensionDeclaration_parameters() {
@@ -853,7 +856,8 @@
         AstTestFactory.extensionDeclaration(
             name: 'E',
             typeParameters: AstTestFactory.typeParameterList(['T']),
-            extendedType: AstTestFactory.typeName4('C')));
+            extendedType: AstTestFactory.typeName4('C'),
+            isExtensionTypeDeclaration: false));
   }
 
   void test_visitExtensionDeclaration_singleMember() {
@@ -865,7 +869,8 @@
             members: [
               AstTestFactory.fieldDeclaration2(
                   false, Keyword.VAR, [AstTestFactory.variableDeclaration('a')])
-            ]));
+            ],
+            isExtensionTypeDeclaration: false));
   }
 
   void test_visitExtensionOverride_prefixedName_noTypeArgs() {
@@ -908,6 +913,53 @@
                 [AstTestFactory.identifier3('o')])));
   }
 
+  void test_visitExtensionTypeDeclaration_empty() {
+    _assertSource(
+        'extension type E on C {}',
+        AstTestFactory.extensionDeclaration(
+            name: 'E',
+            extendedType: AstTestFactory.typeName4('C'),
+            isExtensionTypeDeclaration: true));
+  }
+
+  void test_visitExtensionTypeDeclaration_multipleMember() {
+    _assertSource(
+        'extension type E on C {var a; var b;}',
+        AstTestFactory.extensionDeclaration(
+            name: 'E',
+            extendedType: AstTestFactory.typeName4('C'),
+            members: [
+              AstTestFactory.fieldDeclaration2(false, Keyword.VAR,
+                  [AstTestFactory.variableDeclaration('a')]),
+              AstTestFactory.fieldDeclaration2(
+                  false, Keyword.VAR, [AstTestFactory.variableDeclaration('b')])
+            ],
+            isExtensionTypeDeclaration: true));
+  }
+
+  void test_visitExtensionTypeDeclaration_parameters() {
+    _assertSource(
+        'extension type E<T> on C {}',
+        AstTestFactory.extensionDeclaration(
+            name: 'E',
+            typeParameters: AstTestFactory.typeParameterList(['T']),
+            extendedType: AstTestFactory.typeName4('C'),
+            isExtensionTypeDeclaration: true));
+  }
+
+  void test_visitExtensionTypeDeclaration_singleMember() {
+    _assertSource(
+        'extension type E on C {var a;}',
+        AstTestFactory.extensionDeclaration(
+            name: 'E',
+            extendedType: AstTestFactory.typeName4('C'),
+            members: [
+              AstTestFactory.fieldDeclaration2(
+                  false, Keyword.VAR, [AstTestFactory.variableDeclaration('a')])
+            ],
+            isExtensionTypeDeclaration: true));
+  }
+
   void test_visitFieldDeclaration_abstract() {
     _assertSource(
         "abstract var a;",
diff --git a/pkg/analyzer/test/src/dart/element/element_test.dart b/pkg/analyzer/test/src/dart/element/element_test.dart
index a39f082..4edf45d 100644
--- a/pkg/analyzer/test/src/dart/element/element_test.dart
+++ b/pkg/analyzer/test/src/dart/element/element_test.dart
@@ -77,7 +77,7 @@
     expect(classA.getField(fieldName), same(field));
     expect(field.isEnumConstant, false);
     // no such field
-    expect(classA.getField("noSuchField"), same(null));
+    expect(classA.getField("noSuchField"), isNull);
   }
 
   void test_getMethod_declared() {
diff --git a/pkg/analyzer_plugin/doc/tutorial/package_structure.md b/pkg/analyzer_plugin/doc/tutorial/package_structure.md
index c2443ec..0eb7224 100644
--- a/pkg/analyzer_plugin/doc/tutorial/package_structure.md
+++ b/pkg/analyzer_plugin/doc/tutorial/package_structure.md
@@ -10,7 +10,7 @@
 In order to describe the way tools use plugins, we need to refer to four
 different packages. In order to keep the discussion clear, we will refer to
 those packages as the target package, the host package, the bootstrap package,
-and the plugin package. (If you're not familiar will packages, you should read
+and the plugin package. (If you're not familiar with packages, you should read
 about the Dart [package manager][pub].)
 
 The _target package_ is the package for which the tool is producing analysis
diff --git a/pkg/front_end/lib/src/fasta/source/diet_listener.dart b/pkg/front_end/lib/src/fasta/source/diet_listener.dart
index c5664f8..fb6af76 100644
--- a/pkg/front_end/lib/src/fasta/source/diet_listener.dart
+++ b/pkg/front_end/lib/src/fasta/source/diet_listener.dart
@@ -911,8 +911,8 @@
   }
 
   @override
-  void endExtensionDeclaration(
-      Token extensionKeyword, Token onKeyword, Token endToken) {
+  void endExtensionDeclaration(Token extensionKeyword, Token typeKeyword,
+      Token onKeyword, Token endToken) {
     debugEvent("endExtensionDeclaration");
     checkEmpty(extensionKeyword.charOffset);
   }
diff --git a/pkg/front_end/lib/src/fasta/source/outline_builder.dart b/pkg/front_end/lib/src/fasta/source/outline_builder.dart
index d924fc7..f857afd 100644
--- a/pkg/front_end/lib/src/fasta/source/outline_builder.dart
+++ b/pkg/front_end/lib/src/fasta/source/outline_builder.dart
@@ -752,8 +752,8 @@
   }
 
   @override
-  void endExtensionDeclaration(
-      Token extensionKeyword, Token onKeyword, Token endToken) {
+  void endExtensionDeclaration(Token extensionKeyword, Token typeKeyword,
+      Token onKeyword, Token endToken) {
     assert(checkState(extensionKeyword, [
       unionOfKinds([ValueKinds.ParserRecovery, ValueKinds.TypeBuilder]),
       ValueKinds.TypeVariableListOrNull,
@@ -780,6 +780,15 @@
     int startOffset = metadata == null
         ? extensionKeyword.charOffset
         : metadata.first.charOffset;
+    bool isExtensionTypeDeclaration = typeKeyword != null;
+    if (!libraryBuilder.enableExtensionTypesInLibrary &&
+        isExtensionTypeDeclaration) {
+      addProblem(
+          templateExperimentNotEnabled.withArguments('extension-types',
+              libraryBuilder.enableExtensionTypesVersionInLibrary.toText()),
+          extensionKeyword.next.charOffset,
+          extensionKeyword.next.length);
+    }
     libraryBuilder.addExtensionDeclaration(
         metadata,
         // TODO(johnniwinther): Support modifiers on extensions?
@@ -787,6 +796,7 @@
         name,
         typeVariables,
         onType,
+        isExtensionTypeDeclaration,
         startOffset,
         nameOffset,
         endToken.charOffset);
diff --git a/pkg/front_end/lib/src/fasta/source/source_extension_builder.dart b/pkg/front_end/lib/src/fasta/source/source_extension_builder.dart
index 264a65f..96e2b51 100644
--- a/pkg/front_end/lib/src/fasta/source/source_extension_builder.dart
+++ b/pkg/front_end/lib/src/fasta/source/source_extension_builder.dart
@@ -52,6 +52,7 @@
       TypeBuilder onType,
       Scope scope,
       LibraryBuilder parent,
+      bool isExtensionTypeDeclaration,
       int startOffset,
       int nameOffset,
       int endOffset,
@@ -62,6 +63,7 @@
             typeParameters:
                 TypeVariableBuilder.typeParametersFromBuilders(typeParameters),
             reference: referenceFrom?.reference)
+          ..isExtensionTypeDeclaration = isExtensionTypeDeclaration
           ..fileOffset = nameOffset,
         super(metadata, modifiers, name, parent, nameOffset, scope,
             typeParameters, onType);
diff --git a/pkg/front_end/lib/src/fasta/source/source_library_builder.dart b/pkg/front_end/lib/src/fasta/source/source_library_builder.dart
index 8736d5a..5c72e58 100644
--- a/pkg/front_end/lib/src/fasta/source/source_library_builder.dart
+++ b/pkg/front_end/lib/src/fasta/source/source_library_builder.dart
@@ -314,6 +314,7 @@
   bool _enableNonNullableInLibrary;
   Version _enableNonNullableVersionInLibrary;
   Version _enableConstructorTearoffsVersionInLibrary;
+  Version _enableExtensionTypesVersionInLibrary;
   bool _enableTripleShiftInLibrary;
   bool _enableExtensionMethodsInLibrary;
   bool _enableGenericMetadataInLibrary;
@@ -381,6 +382,11 @@
           _packageUri ?? importUri,
           languageVersion.version);
 
+  Version get enableExtensionTypesVersionInLibrary =>
+      _enableExtensionTypesVersionInLibrary ??= loader.target
+          .getExperimentEnabledVersionInLibrary(
+              ExperimentalFlag.extensionTypes, _packageUri ?? importUri);
+
   void _updateLibraryNNBDSettings() {
     library.isNonNullableByDefault = isNonNullableByDefault;
     switch (loader.nnbdMode) {
@@ -1854,6 +1860,7 @@
       String extensionName,
       List<TypeVariableBuilder> typeVariables,
       TypeBuilder type,
+      bool isExtensionTypeDeclaration,
       int startOffset,
       int nameOffset,
       int endOffset) {
@@ -1887,6 +1894,7 @@
         type,
         classScope,
         this,
+        isExtensionTypeDeclaration,
         startOffset,
         nameOffset,
         endOffset,
diff --git a/pkg/front_end/lib/src/fasta/util/direct_parser_ast_helper.dart b/pkg/front_end/lib/src/fasta/util/direct_parser_ast_helper.dart
index c6cc986..340943c 100644
--- a/pkg/front_end/lib/src/fasta/util/direct_parser_ast_helper.dart
+++ b/pkg/front_end/lib/src/fasta/util/direct_parser_ast_helper.dart
@@ -278,12 +278,13 @@
     seen(data);
   }
 
-  void endExtensionDeclaration(
-      Token extensionKeyword, Token onKeyword, Token endToken) {
+  void endExtensionDeclaration(Token extensionKeyword, Token? typeKeyword,
+      Token onKeyword, Token endToken) {
     DirectParserASTContentExtensionDeclarationEnd data =
         new DirectParserASTContentExtensionDeclarationEnd(
             DirectParserASTType.END,
             extensionKeyword: extensionKeyword,
+            typeKeyword: typeKeyword,
             onKeyword: onKeyword,
             endToken: endToken);
     seen(data);
@@ -2982,17 +2983,20 @@
 class DirectParserASTContentExtensionDeclarationEnd
     extends DirectParserASTContent {
   final Token extensionKeyword;
+  final Token? typeKeyword;
   final Token onKeyword;
   final Token endToken;
 
   DirectParserASTContentExtensionDeclarationEnd(DirectParserASTType type,
       {required this.extensionKeyword,
+      this.typeKeyword,
       required this.onKeyword,
       required this.endToken})
       : super("ExtensionDeclaration", type);
 
   Map<String, Object?> get deprecatedArguments => {
         "extensionKeyword": extensionKeyword,
+        "typeKeyword": typeKeyword,
         "onKeyword": onKeyword,
         "endToken": endToken,
       };
diff --git a/pkg/front_end/lib/src/fasta/util/textual_outline.dart b/pkg/front_end/lib/src/fasta/util/textual_outline.dart
index a658bc4..9af08ca 100644
--- a/pkg/front_end/lib/src/fasta/util/textual_outline.dart
+++ b/pkg/front_end/lib/src/fasta/util/textual_outline.dart
@@ -804,8 +804,8 @@
   }
 
   @override
-  void endExtensionDeclaration(
-      Token extensionKeyword, Token onKeyword, Token endToken) {
+  void endExtensionDeclaration(Token extensionKeyword, Token typeKeyword,
+      Token onKeyword, Token endToken) {
     classStartToChunk[extensionKeyword] =
         new _ExtensionDeclarationChunk(extensionKeyword, endToken);
   }
diff --git a/pkg/front_end/parser_testcases/extension_named_type.dart b/pkg/front_end/parser_testcases/extension_named_type.dart
new file mode 100644
index 0000000..8adcffd
--- /dev/null
+++ b/pkg/front_end/parser_testcases/extension_named_type.dart
@@ -0,0 +1,7 @@
+class A {}
+
+extension type on A {
+  method() {}
+}
+
+test(A a) => type(new A()).method();
diff --git a/pkg/front_end/parser_testcases/extension_named_type.dart.expect b/pkg/front_end/parser_testcases/extension_named_type.dart.expect
new file mode 100644
index 0000000..1d63845
--- /dev/null
+++ b/pkg/front_end/parser_testcases/extension_named_type.dart.expect
@@ -0,0 +1,86 @@
+beginCompilationUnit(class)
+  beginMetadataStar(class)
+  endMetadataStar(0)
+  beginClassOrNamedMixinApplicationPrelude(class)
+    handleIdentifier(A, classOrMixinDeclaration)
+    handleNoTypeVariables({)
+    beginClassDeclaration(class, null, A)
+      handleNoType(A)
+      handleClassExtends(null, 1)
+      handleClassNoWithClause()
+      handleClassOrMixinImplements(null, 0)
+      handleClassHeader(class, class, null)
+      beginClassOrMixinBody(DeclarationKind.Class, {)
+      endClassOrMixinBody(DeclarationKind.Class, 0, {, })
+    endClassDeclaration(class, })
+  endTopLevelDeclaration(extension)
+  beginMetadataStar(extension)
+  endMetadataStar(0)
+  beginExtensionDeclarationPrelude(extension)
+    handleNoTypeVariables(on)
+    beginExtensionDeclaration(extension, type)
+      handleIdentifier(A, typeReference)
+      handleNoTypeArguments({)
+      handleType(A, null)
+      beginClassOrMixinBody(DeclarationKind.Extension, {)
+        beginMetadataStar(method)
+        endMetadataStar(0)
+        beginMember()
+          beginMethod(null, null, null, null, null, method)
+            handleNoType({)
+            handleIdentifier(method, methodDeclaration)
+            handleNoTypeVariables(()
+            beginFormalParameters((, MemberKind.ExtensionNonStaticMethod)
+            endFormalParameters(0, (, ), MemberKind.ExtensionNonStaticMethod)
+            handleNoInitializers()
+            handleAsyncModifier(null, null)
+            beginBlockFunctionBody({)
+            endBlockFunctionBody(0, {, })
+          endExtensionMethod(null, method, (, null, })
+        endMember()
+      endClassOrMixinBody(DeclarationKind.Extension, 1, {, })
+    endExtensionDeclaration(extension, null, on, })
+  endTopLevelDeclaration(test)
+  beginMetadataStar(test)
+  endMetadataStar(0)
+  beginTopLevelMember(test)
+    beginTopLevelMethod(}, null)
+      handleNoType(})
+      handleIdentifier(test, topLevelFunctionDeclaration)
+      handleNoTypeVariables(()
+      beginFormalParameters((, MemberKind.TopLevelMethod)
+        beginMetadataStar(A)
+        endMetadataStar(0)
+        beginFormalParameter(A, MemberKind.TopLevelMethod, null, null, null)
+          handleIdentifier(A, typeReference)
+          handleNoTypeArguments(a)
+          handleType(A, null)
+          handleIdentifier(a, formalParameterDeclaration)
+          handleFormalParameterWithoutValue())
+        endFormalParameter(null, null, a, null, null, FormalParameterKind.mandatory, MemberKind.TopLevelMethod)
+      endFormalParameters(1, (, ), MemberKind.TopLevelMethod)
+      handleAsyncModifier(null, null)
+      handleIdentifier(type, expression)
+      handleNoTypeArguments(()
+      beginArguments(()
+        beginNewExpression(new)
+          handleIdentifier(A, constructorReference)
+          beginConstructorReference(A)
+            handleNoTypeArguments(()
+            handleNoConstructorReferenceContinuationAfterTypeArguments(()
+          endConstructorReference(A, null, ()
+          beginArguments(()
+          endArguments(0, (, ))
+        endNewExpression(new)
+      endArguments(1, (, ))
+      handleSend(type, .)
+      handleIdentifier(method, expressionContinuation)
+      handleNoTypeArguments(()
+      beginArguments(()
+      endArguments(0, (, ))
+      handleSend(method, ;)
+      handleEndingBinaryExpression(.)
+      handleExpressionFunctionBody(=>, ;)
+    endTopLevelMethod(test, null, ;)
+  endTopLevelDeclaration()
+endCompilationUnit(3, )
diff --git a/pkg/front_end/parser_testcases/extension_named_type.dart.intertwined.expect b/pkg/front_end/parser_testcases/extension_named_type.dart.intertwined.expect
new file mode 100644
index 0000000..ac6be71
--- /dev/null
+++ b/pkg/front_end/parser_testcases/extension_named_type.dart.intertwined.expect
@@ -0,0 +1,178 @@
+parseUnit(class)
+  skipErrorTokens(class)
+  listener: beginCompilationUnit(class)
+  syntheticPreviousToken(class)
+  parseTopLevelDeclarationImpl(, Instance of 'DirectiveContext')
+    parseMetadataStar()
+      listener: beginMetadataStar(class)
+      listener: endMetadataStar(0)
+    parseTopLevelKeywordDeclaration(, class, Instance of 'DirectiveContext')
+      parseClassDeclarationModifiers(, class)
+      parseClassOrNamedMixinApplication(null, class)
+        listener: beginClassOrNamedMixinApplicationPrelude(class)
+        ensureIdentifier(class, classOrMixinDeclaration)
+          listener: handleIdentifier(A, classOrMixinDeclaration)
+        listener: handleNoTypeVariables({)
+        listener: beginClassDeclaration(class, null, A)
+        parseClass(A, class, class, A)
+          parseClassHeaderOpt(A, class, class)
+            parseClassExtendsOpt(A)
+              listener: handleNoType(A)
+              listener: handleClassExtends(null, 1)
+            parseWithClauseOpt(A)
+              listener: handleClassNoWithClause()
+            parseClassOrMixinImplementsOpt(A)
+              listener: handleClassOrMixinImplements(null, 0)
+            listener: handleClassHeader(class, class, null)
+          parseClassOrMixinOrExtensionBody(A, DeclarationKind.Class, A)
+            listener: beginClassOrMixinBody(DeclarationKind.Class, {)
+            notEofOrValue(}, })
+            listener: endClassOrMixinBody(DeclarationKind.Class, 0, {, })
+          listener: endClassDeclaration(class, })
+  listener: endTopLevelDeclaration(extension)
+  parseTopLevelDeclarationImpl(}, Instance of 'DirectiveContext')
+    parseMetadataStar(})
+      listener: beginMetadataStar(extension)
+      listener: endMetadataStar(0)
+    parseTopLevelKeywordDeclaration(}, extension, Instance of 'DirectiveContext')
+      parseTopLevelKeywordModifiers(}, extension)
+      parseExtension(extension)
+        listener: beginExtensionDeclarationPrelude(extension)
+        listener: handleNoTypeVariables(on)
+        listener: beginExtensionDeclaration(extension, type)
+        listener: handleIdentifier(A, typeReference)
+        listener: handleNoTypeArguments({)
+        listener: handleType(A, null)
+        parseClassOrMixinOrExtensionBody(A, DeclarationKind.Extension, type)
+          listener: beginClassOrMixinBody(DeclarationKind.Extension, {)
+          notEofOrValue(}, method)
+          parseClassOrMixinOrExtensionMemberImpl({, DeclarationKind.Extension, type)
+            parseMetadataStar({)
+              listener: beginMetadataStar(method)
+              listener: endMetadataStar(0)
+            listener: beginMember()
+            isReservedKeyword(()
+            parseMethod({, null, null, null, null, null, null, {, Instance of 'NoType', null, method, DeclarationKind.Extension, type, false)
+              listener: beginMethod(null, null, null, null, null, method)
+              listener: handleNoType({)
+              ensureIdentifierPotentiallyRecovered({, methodDeclaration, false)
+                listener: handleIdentifier(method, methodDeclaration)
+              parseQualifiedRestOpt(method, methodDeclarationContinuation)
+              parseMethodTypeVar(method)
+                listener: handleNoTypeVariables(()
+              parseGetterOrFormalParameters(method, method, false, MemberKind.ExtensionNonStaticMethod)
+                parseFormalParameters(method, MemberKind.ExtensionNonStaticMethod)
+                  parseFormalParametersRest((, MemberKind.ExtensionNonStaticMethod)
+                    listener: beginFormalParameters((, MemberKind.ExtensionNonStaticMethod)
+                    listener: endFormalParameters(0, (, ), MemberKind.ExtensionNonStaticMethod)
+              parseInitializersOpt())
+                listener: handleNoInitializers()
+              parseAsyncModifierOpt())
+                listener: handleAsyncModifier(null, null)
+                inPlainSync()
+              inPlainSync()
+              parseFunctionBody(), false, true)
+                listener: beginBlockFunctionBody({)
+                notEofOrValue(}, })
+                listener: endBlockFunctionBody(0, {, })
+              listener: endExtensionMethod(null, method, (, null, })
+            listener: endMember()
+          notEofOrValue(}, })
+          listener: endClassOrMixinBody(DeclarationKind.Extension, 1, {, })
+        listener: endExtensionDeclaration(extension, null, on, })
+  listener: endTopLevelDeclaration(test)
+  parseTopLevelDeclarationImpl(}, Instance of 'DirectiveContext')
+    parseMetadataStar(})
+      listener: beginMetadataStar(test)
+      listener: endMetadataStar(0)
+    parseTopLevelMemberImpl(})
+      listener: beginTopLevelMember(test)
+      isReservedKeyword(()
+      parseTopLevelMethod(}, null, }, Instance of 'NoType', null, test, false)
+        listener: beginTopLevelMethod(}, null)
+        listener: handleNoType(})
+        ensureIdentifierPotentiallyRecovered(}, topLevelFunctionDeclaration, false)
+          listener: handleIdentifier(test, topLevelFunctionDeclaration)
+        parseMethodTypeVar(test)
+          listener: handleNoTypeVariables(()
+        parseGetterOrFormalParameters(test, test, false, MemberKind.TopLevelMethod)
+          parseFormalParameters(test, MemberKind.TopLevelMethod)
+            parseFormalParametersRest((, MemberKind.TopLevelMethod)
+              listener: beginFormalParameters((, MemberKind.TopLevelMethod)
+              parseFormalParameter((, FormalParameterKind.mandatory, MemberKind.TopLevelMethod)
+                parseMetadataStar(()
+                  listener: beginMetadataStar(A)
+                  listener: endMetadataStar(0)
+                listener: beginFormalParameter(A, MemberKind.TopLevelMethod, null, null, null)
+                listener: handleIdentifier(A, typeReference)
+                listener: handleNoTypeArguments(a)
+                listener: handleType(A, null)
+                ensureIdentifier(A, formalParameterDeclaration)
+                  listener: handleIdentifier(a, formalParameterDeclaration)
+                listener: handleFormalParameterWithoutValue())
+                listener: endFormalParameter(null, null, a, null, null, FormalParameterKind.mandatory, MemberKind.TopLevelMethod)
+              listener: endFormalParameters(1, (, ), MemberKind.TopLevelMethod)
+        parseAsyncModifierOpt())
+          listener: handleAsyncModifier(null, null)
+          inPlainSync()
+        parseFunctionBody(), false, false)
+          parseExpressionFunctionBody(=>, false)
+            parseExpression(=>)
+              parsePrecedenceExpression(=>, 1, true)
+                parseUnaryExpression(=>, true)
+                  parsePrimary(=>, expression)
+                    parseSendOrFunctionLiteral(=>, expression)
+                      looksLikeFunctionBody(.)
+                      parseSend(=>, expression)
+                        isNextIdentifier(=>)
+                        ensureIdentifier(=>, expression)
+                          listener: handleIdentifier(type, expression)
+                        listener: handleNoTypeArguments(()
+                        parseArgumentsOpt(type)
+                          parseArguments(type)
+                            parseArgumentsRest(()
+                              listener: beginArguments(()
+                              parseExpression(()
+                                parsePrecedenceExpression((, 1, true)
+                                  parseUnaryExpression((, true)
+                                    parsePrimary((, expression)
+                                      parseNewExpression(()
+                                        isNextIdentifier(new)
+                                        listener: beginNewExpression(new)
+                                        parseConstructorReference(new, null)
+                                          ensureIdentifier(new, constructorReference)
+                                            listener: handleIdentifier(A, constructorReference)
+                                          listener: beginConstructorReference(A)
+                                          parseQualifiedRestOpt(A, constructorReferenceContinuation)
+                                          listener: handleNoTypeArguments(()
+                                          listener: handleNoConstructorReferenceContinuationAfterTypeArguments(()
+                                          listener: endConstructorReference(A, null, ()
+                                        parseConstructorInvocationArguments(A)
+                                          parseArgumentsRest(()
+                                            listener: beginArguments(()
+                                            listener: endArguments(0, (, ))
+                                        listener: endNewExpression(new)
+                              listener: endArguments(1, (, ))
+                        listener: handleSend(type, .)
+                parsePrimary(., expressionContinuation)
+                  parseSendOrFunctionLiteral(., expressionContinuation)
+                    looksLikeFunctionBody(;)
+                    parseSend(., expressionContinuation)
+                      isNextIdentifier(.)
+                      ensureIdentifier(., expressionContinuation)
+                        listener: handleIdentifier(method, expressionContinuation)
+                      listener: handleNoTypeArguments(()
+                      parseArgumentsOpt(method)
+                        parseArguments(method)
+                          parseArgumentsRest(()
+                            listener: beginArguments(()
+                            listener: endArguments(0, (, ))
+                      listener: handleSend(method, ;)
+                listener: handleEndingBinaryExpression(.)
+            ensureSemicolon())
+            listener: handleExpressionFunctionBody(=>, ;)
+            inGenerator()
+        listener: endTopLevelMethod(test, null, ;)
+  listener: endTopLevelDeclaration()
+  reportAllErrorTokens(class)
+  listener: endCompilationUnit(3, )
diff --git a/pkg/front_end/parser_testcases/extension_named_type.dart.parser.expect b/pkg/front_end/parser_testcases/extension_named_type.dart.parser.expect
new file mode 100644
index 0000000..04658d0
--- /dev/null
+++ b/pkg/front_end/parser_testcases/extension_named_type.dart.parser.expect
@@ -0,0 +1,17 @@
+class A {}
+
+extension type on A {
+method() {}
+}
+
+test(A a) => type(new A()).method();
+
+
+class[KeywordToken] A[StringToken] {[BeginToken]}[SimpleToken]
+
+extension[KeywordToken] type[StringToken] on[KeywordToken] A[StringToken] {[BeginToken]
+method[StringToken]([BeginToken])[SimpleToken] {[BeginToken]}[SimpleToken]
+}[SimpleToken]
+
+test[StringToken]([BeginToken]A[StringToken] a[StringToken])[SimpleToken] =>[SimpleToken] type[StringToken]([BeginToken]new[KeywordToken] A[StringToken]([BeginToken])[SimpleToken])[SimpleToken].[SimpleToken]method[StringToken]([BeginToken])[SimpleToken];[SimpleToken]
+[SimpleToken]
diff --git a/pkg/front_end/parser_testcases/extension_named_type.dart.scanner.expect b/pkg/front_end/parser_testcases/extension_named_type.dart.scanner.expect
new file mode 100644
index 0000000..04658d0
--- /dev/null
+++ b/pkg/front_end/parser_testcases/extension_named_type.dart.scanner.expect
@@ -0,0 +1,17 @@
+class A {}
+
+extension type on A {
+method() {}
+}
+
+test(A a) => type(new A()).method();
+
+
+class[KeywordToken] A[StringToken] {[BeginToken]}[SimpleToken]
+
+extension[KeywordToken] type[StringToken] on[KeywordToken] A[StringToken] {[BeginToken]
+method[StringToken]([BeginToken])[SimpleToken] {[BeginToken]}[SimpleToken]
+}[SimpleToken]
+
+test[StringToken]([BeginToken]A[StringToken] a[StringToken])[SimpleToken] =>[SimpleToken] type[StringToken]([BeginToken]new[KeywordToken] A[StringToken]([BeginToken])[SimpleToken])[SimpleToken].[SimpleToken]method[StringToken]([BeginToken])[SimpleToken];[SimpleToken]
+[SimpleToken]
diff --git a/pkg/front_end/parser_testcases/extension_type.dart b/pkg/front_end/parser_testcases/extension_type.dart
new file mode 100644
index 0000000..2a86715
--- /dev/null
+++ b/pkg/front_end/parser_testcases/extension_type.dart
@@ -0,0 +1,3 @@
+class A {}
+
+extension type E on A {}
diff --git a/pkg/front_end/parser_testcases/extension_type.dart.expect b/pkg/front_end/parser_testcases/extension_type.dart.expect
new file mode 100644
index 0000000..c759acc
--- /dev/null
+++ b/pkg/front_end/parser_testcases/extension_type.dart.expect
@@ -0,0 +1,29 @@
+beginCompilationUnit(class)
+  beginMetadataStar(class)
+  endMetadataStar(0)
+  beginClassOrNamedMixinApplicationPrelude(class)
+    handleIdentifier(A, classOrMixinDeclaration)
+    handleNoTypeVariables({)
+    beginClassDeclaration(class, null, A)
+      handleNoType(A)
+      handleClassExtends(null, 1)
+      handleClassNoWithClause()
+      handleClassOrMixinImplements(null, 0)
+      handleClassHeader(class, class, null)
+      beginClassOrMixinBody(DeclarationKind.Class, {)
+      endClassOrMixinBody(DeclarationKind.Class, 0, {, })
+    endClassDeclaration(class, })
+  endTopLevelDeclaration(extension)
+  beginMetadataStar(extension)
+  endMetadataStar(0)
+  beginExtensionDeclarationPrelude(extension)
+    handleNoTypeVariables(on)
+    beginExtensionDeclaration(extension, E)
+      handleIdentifier(A, typeReference)
+      handleNoTypeArguments({)
+      handleType(A, null)
+      beginClassOrMixinBody(DeclarationKind.Extension, {)
+      endClassOrMixinBody(DeclarationKind.Extension, 0, {, })
+    endExtensionDeclaration(extension, type, on, })
+  endTopLevelDeclaration()
+endCompilationUnit(2, )
diff --git a/pkg/front_end/parser_testcases/extension_type.dart.intertwined.expect b/pkg/front_end/parser_testcases/extension_type.dart.intertwined.expect
new file mode 100644
index 0000000..c8f125d
--- /dev/null
+++ b/pkg/front_end/parser_testcases/extension_type.dart.intertwined.expect
@@ -0,0 +1,53 @@
+parseUnit(class)
+  skipErrorTokens(class)
+  listener: beginCompilationUnit(class)
+  syntheticPreviousToken(class)
+  parseTopLevelDeclarationImpl(, Instance of 'DirectiveContext')
+    parseMetadataStar()
+      listener: beginMetadataStar(class)
+      listener: endMetadataStar(0)
+    parseTopLevelKeywordDeclaration(, class, Instance of 'DirectiveContext')
+      parseClassDeclarationModifiers(, class)
+      parseClassOrNamedMixinApplication(null, class)
+        listener: beginClassOrNamedMixinApplicationPrelude(class)
+        ensureIdentifier(class, classOrMixinDeclaration)
+          listener: handleIdentifier(A, classOrMixinDeclaration)
+        listener: handleNoTypeVariables({)
+        listener: beginClassDeclaration(class, null, A)
+        parseClass(A, class, class, A)
+          parseClassHeaderOpt(A, class, class)
+            parseClassExtendsOpt(A)
+              listener: handleNoType(A)
+              listener: handleClassExtends(null, 1)
+            parseWithClauseOpt(A)
+              listener: handleClassNoWithClause()
+            parseClassOrMixinImplementsOpt(A)
+              listener: handleClassOrMixinImplements(null, 0)
+            listener: handleClassHeader(class, class, null)
+          parseClassOrMixinOrExtensionBody(A, DeclarationKind.Class, A)
+            listener: beginClassOrMixinBody(DeclarationKind.Class, {)
+            notEofOrValue(}, })
+            listener: endClassOrMixinBody(DeclarationKind.Class, 0, {, })
+          listener: endClassDeclaration(class, })
+  listener: endTopLevelDeclaration(extension)
+  parseTopLevelDeclarationImpl(}, Instance of 'DirectiveContext')
+    parseMetadataStar(})
+      listener: beginMetadataStar(extension)
+      listener: endMetadataStar(0)
+    parseTopLevelKeywordDeclaration(}, extension, Instance of 'DirectiveContext')
+      parseTopLevelKeywordModifiers(}, extension)
+      parseExtension(extension)
+        listener: beginExtensionDeclarationPrelude(extension)
+        listener: handleNoTypeVariables(on)
+        listener: beginExtensionDeclaration(extension, E)
+        listener: handleIdentifier(A, typeReference)
+        listener: handleNoTypeArguments({)
+        listener: handleType(A, null)
+        parseClassOrMixinOrExtensionBody(A, DeclarationKind.Extension, E)
+          listener: beginClassOrMixinBody(DeclarationKind.Extension, {)
+          notEofOrValue(}, })
+          listener: endClassOrMixinBody(DeclarationKind.Extension, 0, {, })
+        listener: endExtensionDeclaration(extension, type, on, })
+  listener: endTopLevelDeclaration()
+  reportAllErrorTokens(class)
+  listener: endCompilationUnit(2, )
diff --git a/pkg/front_end/parser_testcases/extension_type.dart.parser.expect b/pkg/front_end/parser_testcases/extension_type.dart.parser.expect
new file mode 100644
index 0000000..0a015a7
--- /dev/null
+++ b/pkg/front_end/parser_testcases/extension_type.dart.parser.expect
@@ -0,0 +1,9 @@
+class A {}
+
+extension type E on A {}
+
+
+class[KeywordToken] A[StringToken] {[BeginToken]}[SimpleToken]
+
+extension[KeywordToken] type[StringToken] E[StringToken] on[KeywordToken] A[StringToken] {[BeginToken]}[SimpleToken]
+[SimpleToken]
diff --git a/pkg/front_end/parser_testcases/extension_type.dart.scanner.expect b/pkg/front_end/parser_testcases/extension_type.dart.scanner.expect
new file mode 100644
index 0000000..0a015a7
--- /dev/null
+++ b/pkg/front_end/parser_testcases/extension_type.dart.scanner.expect
@@ -0,0 +1,9 @@
+class A {}
+
+extension type E on A {}
+
+
+class[KeywordToken] A[StringToken] {[BeginToken]}[SimpleToken]
+
+extension[KeywordToken] type[StringToken] E[StringToken] on[KeywordToken] A[StringToken] {[BeginToken]}[SimpleToken]
+[SimpleToken]
diff --git a/pkg/front_end/parser_testcases/extensions/covariant.dart.expect b/pkg/front_end/parser_testcases/extensions/covariant.dart.expect
index 9afdfed..3cc233a 100644
--- a/pkg/front_end/parser_testcases/extensions/covariant.dart.expect
+++ b/pkg/front_end/parser_testcases/extensions/covariant.dart.expect
@@ -72,6 +72,6 @@
           endExtensionMethod(null, addChild, (, null, })
         endMember()
       endClassOrMixinBody(DeclarationKind.Extension, 1, {, })
-    endExtensionDeclaration(extension, on, })
+    endExtensionDeclaration(extension, null, on, })
   endTopLevelDeclaration()
 endCompilationUnit(3, )
diff --git a/pkg/front_end/parser_testcases/extensions/covariant.dart.intertwined.expect b/pkg/front_end/parser_testcases/extensions/covariant.dart.intertwined.expect
index a18a857..b493885 100644
--- a/pkg/front_end/parser_testcases/extensions/covariant.dart.intertwined.expect
+++ b/pkg/front_end/parser_testcases/extensions/covariant.dart.intertwined.expect
@@ -124,7 +124,7 @@
             listener: endMember()
           notEofOrValue(}, })
           listener: endClassOrMixinBody(DeclarationKind.Extension, 1, {, })
-        listener: endExtensionDeclaration(extension, on, })
+        listener: endExtensionDeclaration(extension, null, on, })
   listener: endTopLevelDeclaration()
   reportAllErrorTokens(class)
   listener: endCompilationUnit(3, )
diff --git a/pkg/front_end/parser_testcases/extensions/not_covariant.dart.expect b/pkg/front_end/parser_testcases/extensions/not_covariant.dart.expect
index 4bdbe33..b6adb53 100644
--- a/pkg/front_end/parser_testcases/extensions/not_covariant.dart.expect
+++ b/pkg/front_end/parser_testcases/extensions/not_covariant.dart.expect
@@ -65,6 +65,6 @@
           endExtensionMethod(null, addChild, (, null, })
         endMember()
       endClassOrMixinBody(DeclarationKind.Extension, 1, {, })
-    endExtensionDeclaration(extension, on, })
+    endExtensionDeclaration(extension, null, on, })
   endTopLevelDeclaration()
 endCompilationUnit(3, )
diff --git a/pkg/front_end/parser_testcases/extensions/not_covariant.dart.intertwined.expect b/pkg/front_end/parser_testcases/extensions/not_covariant.dart.intertwined.expect
index 27c8d34..333ccf1 100644
--- a/pkg/front_end/parser_testcases/extensions/not_covariant.dart.intertwined.expect
+++ b/pkg/front_end/parser_testcases/extensions/not_covariant.dart.intertwined.expect
@@ -122,7 +122,7 @@
             listener: endMember()
           notEofOrValue(}, })
           listener: endClassOrMixinBody(DeclarationKind.Extension, 1, {, })
-        listener: endExtensionDeclaration(extension, on, })
+        listener: endExtensionDeclaration(extension, null, on, })
   listener: endTopLevelDeclaration()
   reportAllErrorTokens(class)
   listener: endCompilationUnit(3, )
diff --git a/pkg/front_end/parser_testcases/extensions/static.dart.expect b/pkg/front_end/parser_testcases/extensions/static.dart.expect
index 9579c65..568639a 100644
--- a/pkg/front_end/parser_testcases/extensions/static.dart.expect
+++ b/pkg/front_end/parser_testcases/extensions/static.dart.expect
@@ -65,6 +65,6 @@
           endExtensionMethod(null, static, (, null, })
         endMember()
       endClassOrMixinBody(DeclarationKind.Extension, 1, {, })
-    endExtensionDeclaration(extension, on, })
+    endExtensionDeclaration(extension, null, on, })
   endTopLevelDeclaration()
 endCompilationUnit(3, )
diff --git a/pkg/front_end/parser_testcases/extensions/static.dart.intertwined.expect b/pkg/front_end/parser_testcases/extensions/static.dart.intertwined.expect
index 2824804..f91d314 100644
--- a/pkg/front_end/parser_testcases/extensions/static.dart.intertwined.expect
+++ b/pkg/front_end/parser_testcases/extensions/static.dart.intertwined.expect
@@ -121,7 +121,7 @@
             listener: endMember()
           notEofOrValue(}, })
           listener: endClassOrMixinBody(DeclarationKind.Extension, 1, {, })
-        listener: endExtensionDeclaration(extension, on, })
+        listener: endExtensionDeclaration(extension, null, on, })
   listener: endTopLevelDeclaration()
   reportAllErrorTokens(class)
   listener: endCompilationUnit(3, )
diff --git a/pkg/front_end/parser_testcases/extensions/static_covariant.dart.expect b/pkg/front_end/parser_testcases/extensions/static_covariant.dart.expect
index 57bc44a..1e1a39a 100644
--- a/pkg/front_end/parser_testcases/extensions/static_covariant.dart.expect
+++ b/pkg/front_end/parser_testcases/extensions/static_covariant.dart.expect
@@ -72,6 +72,6 @@
           endExtensionMethod(null, static, (, null, })
         endMember()
       endClassOrMixinBody(DeclarationKind.Extension, 1, {, })
-    endExtensionDeclaration(extension, on, })
+    endExtensionDeclaration(extension, null, on, })
   endTopLevelDeclaration()
 endCompilationUnit(3, )
diff --git a/pkg/front_end/parser_testcases/extensions/static_covariant.dart.intertwined.expect b/pkg/front_end/parser_testcases/extensions/static_covariant.dart.intertwined.expect
index e867360..c1ae5d5 100644
--- a/pkg/front_end/parser_testcases/extensions/static_covariant.dart.intertwined.expect
+++ b/pkg/front_end/parser_testcases/extensions/static_covariant.dart.intertwined.expect
@@ -123,7 +123,7 @@
             listener: endMember()
           notEofOrValue(}, })
           listener: endClassOrMixinBody(DeclarationKind.Extension, 1, {, })
-        listener: endExtensionDeclaration(extension, on, })
+        listener: endExtensionDeclaration(extension, null, on, })
   listener: endTopLevelDeclaration()
   reportAllErrorTokens(class)
   listener: endCompilationUnit(3, )
diff --git a/pkg/front_end/test/parser_test_listener.dart b/pkg/front_end/test/parser_test_listener.dart
index 824db8e..cb76069 100644
--- a/pkg/front_end/test/parser_test_listener.dart
+++ b/pkg/front_end/test/parser_test_listener.dart
@@ -265,14 +265,16 @@
     indent++;
   }
 
-  void endExtensionDeclaration(
-      Token extensionKeyword, Token onKeyword, Token endToken) {
+  void endExtensionDeclaration(Token extensionKeyword, Token? typeKeyword,
+      Token onKeyword, Token endToken) {
     indent--;
     seen(extensionKeyword);
+    seen(typeKeyword);
     seen(onKeyword);
     seen(endToken);
     doPrint('endExtensionDeclaration('
         '$extensionKeyword, '
+        '$typeKeyword, '
         '$onKeyword, '
         '$endToken)');
   }
diff --git a/pkg/front_end/testcases/extension_types/extension_with_name_type.dart b/pkg/front_end/testcases/extension_types/extension_with_name_type.dart
new file mode 100644
index 0000000..6655bc8
--- /dev/null
+++ b/pkg/front_end/testcases/extension_types/extension_with_name_type.dart
@@ -0,0 +1,15 @@
+// Copyright (c) 2021, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+class A {}
+
+extension type on A {
+  method() {}
+}
+
+test() {
+  type(new A()).method(); // Ok.
+}
+
+main() {}
diff --git a/pkg/front_end/testcases/extension_types/extension_with_name_type.dart.strong.expect b/pkg/front_end/testcases/extension_types/extension_with_name_type.dart.strong.expect
new file mode 100644
index 0000000..626118a
--- /dev/null
+++ b/pkg/front_end/testcases/extension_types/extension_with_name_type.dart.strong.expect
@@ -0,0 +1,20 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+class A extends core::Object {
+  synthetic constructor •() → self::A
+    : super core::Object::•()
+    ;
+}
+extension type on self::A {
+  method method = self::type|method;
+  tearoff method = self::type|get#method;
+}
+static method type|method(lowered final self::A #this) → dynamic {}
+static method type|get#method(lowered final self::A #this) → () → dynamic
+  return () → dynamic => self::type|method(#this);
+static method test() → dynamic {
+  self::type|method(new self::A::•());
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/extension_types/extension_with_name_type.dart.strong.transformed.expect b/pkg/front_end/testcases/extension_types/extension_with_name_type.dart.strong.transformed.expect
new file mode 100644
index 0000000..626118a
--- /dev/null
+++ b/pkg/front_end/testcases/extension_types/extension_with_name_type.dart.strong.transformed.expect
@@ -0,0 +1,20 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+class A extends core::Object {
+  synthetic constructor •() → self::A
+    : super core::Object::•()
+    ;
+}
+extension type on self::A {
+  method method = self::type|method;
+  tearoff method = self::type|get#method;
+}
+static method type|method(lowered final self::A #this) → dynamic {}
+static method type|get#method(lowered final self::A #this) → () → dynamic
+  return () → dynamic => self::type|method(#this);
+static method test() → dynamic {
+  self::type|method(new self::A::•());
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/extension_types/extension_with_name_type.dart.textual_outline.expect b/pkg/front_end/testcases/extension_types/extension_with_name_type.dart.textual_outline.expect
new file mode 100644
index 0000000..bbbe190aa
--- /dev/null
+++ b/pkg/front_end/testcases/extension_types/extension_with_name_type.dart.textual_outline.expect
@@ -0,0 +1,8 @@
+class A {}
+
+extension type on A {
+  method() {}
+}
+
+test() {}
+main() {}
diff --git a/pkg/front_end/testcases/extension_types/extension_with_name_type.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/extension_types/extension_with_name_type.dart.textual_outline_modelled.expect
new file mode 100644
index 0000000..310a117
--- /dev/null
+++ b/pkg/front_end/testcases/extension_types/extension_with_name_type.dart.textual_outline_modelled.expect
@@ -0,0 +1,8 @@
+class A {}
+
+extension type on A {
+  method() {}
+}
+
+main() {}
+test() {}
diff --git a/pkg/front_end/testcases/extension_types/extension_with_name_type.dart.weak.expect b/pkg/front_end/testcases/extension_types/extension_with_name_type.dart.weak.expect
new file mode 100644
index 0000000..626118a
--- /dev/null
+++ b/pkg/front_end/testcases/extension_types/extension_with_name_type.dart.weak.expect
@@ -0,0 +1,20 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+class A extends core::Object {
+  synthetic constructor •() → self::A
+    : super core::Object::•()
+    ;
+}
+extension type on self::A {
+  method method = self::type|method;
+  tearoff method = self::type|get#method;
+}
+static method type|method(lowered final self::A #this) → dynamic {}
+static method type|get#method(lowered final self::A #this) → () → dynamic
+  return () → dynamic => self::type|method(#this);
+static method test() → dynamic {
+  self::type|method(new self::A::•());
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/extension_types/extension_with_name_type.dart.weak.outline.expect b/pkg/front_end/testcases/extension_types/extension_with_name_type.dart.weak.outline.expect
new file mode 100644
index 0000000..a3848a4
--- /dev/null
+++ b/pkg/front_end/testcases/extension_types/extension_with_name_type.dart.weak.outline.expect
@@ -0,0 +1,20 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+class A extends core::Object {
+  synthetic constructor •() → self::A
+    ;
+}
+extension type on self::A {
+  method method = self::type|method;
+  tearoff method = self::type|get#method;
+}
+static method type|method(lowered final self::A #this) → dynamic
+  ;
+static method type|get#method(lowered final self::A #this) → () → dynamic
+  return () → dynamic => self::type|method(#this);
+static method test() → dynamic
+  ;
+static method main() → dynamic
+  ;
diff --git a/pkg/front_end/testcases/extension_types/extension_with_name_type.dart.weak.transformed.expect b/pkg/front_end/testcases/extension_types/extension_with_name_type.dart.weak.transformed.expect
new file mode 100644
index 0000000..626118a
--- /dev/null
+++ b/pkg/front_end/testcases/extension_types/extension_with_name_type.dart.weak.transformed.expect
@@ -0,0 +1,20 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+class A extends core::Object {
+  synthetic constructor •() → self::A
+    : super core::Object::•()
+    ;
+}
+extension type on self::A {
+  method method = self::type|method;
+  tearoff method = self::type|get#method;
+}
+static method type|method(lowered final self::A #this) → dynamic {}
+static method type|get#method(lowered final self::A #this) → () → dynamic
+  return () → dynamic => self::type|method(#this);
+static method test() → dynamic {
+  self::type|method(new self::A::•());
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/general/extension_types_feature_not_enabled.dart b/pkg/front_end/testcases/general/extension_types_feature_not_enabled.dart
new file mode 100644
index 0000000..e1ee63d
--- /dev/null
+++ b/pkg/front_end/testcases/general/extension_types_feature_not_enabled.dart
@@ -0,0 +1,11 @@
+// Copyright (c) 2021, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// Remove this file when 'extension-types' is enabled by default.
+
+class A {}
+
+extension type E on A {} // Error because of 'type'.
+
+main() {}
diff --git a/pkg/front_end/testcases/general/extension_types_feature_not_enabled.dart.textual_outline.expect b/pkg/front_end/testcases/general/extension_types_feature_not_enabled.dart.textual_outline.expect
new file mode 100644
index 0000000..a4c015d
--- /dev/null
+++ b/pkg/front_end/testcases/general/extension_types_feature_not_enabled.dart.textual_outline.expect
@@ -0,0 +1,3 @@
+class A {}
+extension type E on A {}
+main() {}
diff --git a/pkg/front_end/testcases/general/extension_types_feature_not_enabled.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/general/extension_types_feature_not_enabled.dart.textual_outline_modelled.expect
new file mode 100644
index 0000000..f4eb2498
--- /dev/null
+++ b/pkg/front_end/testcases/general/extension_types_feature_not_enabled.dart.textual_outline_modelled.expect
@@ -0,0 +1,5 @@
+class A {}
+
+extension type on A {}
+
+main() {}
diff --git a/pkg/front_end/testcases/general/extension_types_feature_not_enabled.dart.weak.expect b/pkg/front_end/testcases/general/extension_types_feature_not_enabled.dart.weak.expect
new file mode 100644
index 0000000..14f7d5b
--- /dev/null
+++ b/pkg/front_end/testcases/general/extension_types_feature_not_enabled.dart.weak.expect
@@ -0,0 +1,20 @@
+library /*isNonNullableByDefault*/;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/general/extension_types_feature_not_enabled.dart:9:11: Error: This requires the 'extension-types' language feature to be enabled.
+// Try updating your pubspec.yaml to set the minimum SDK constraint to 2.14 or higher, and running 'pub get'.
+// extension type E on A {} // Error because of 'type'.
+//           ^^^^
+//
+import self as self;
+import "dart:core" as core;
+
+class A extends core::Object {
+  synthetic constructor •() → self::A
+    : super core::Object::•()
+    ;
+}
+extension type E on self::A {
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/general/extension_types_feature_not_enabled.dart.weak.outline.expect b/pkg/front_end/testcases/general/extension_types_feature_not_enabled.dart.weak.outline.expect
new file mode 100644
index 0000000..6a94860
--- /dev/null
+++ b/pkg/front_end/testcases/general/extension_types_feature_not_enabled.dart.weak.outline.expect
@@ -0,0 +1,20 @@
+library /*isNonNullableByDefault*/;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/general/extension_types_feature_not_enabled.dart:9:11: Error: This requires the 'extension-types' language feature to be enabled.
+// Try updating your pubspec.yaml to set the minimum SDK constraint to 2.14 or higher, and running 'pub get'.
+// extension type E on A {} // Error because of 'type'.
+//           ^^^^
+//
+import self as self;
+import "dart:core" as core;
+
+class A extends core::Object {
+  synthetic constructor •() → self::A
+    ;
+}
+extension type E on self::A {
+}
+static method main() → dynamic
+  ;
diff --git a/pkg/front_end/testcases/general/extension_types_feature_not_enabled.dart.weak.transformed.expect b/pkg/front_end/testcases/general/extension_types_feature_not_enabled.dart.weak.transformed.expect
new file mode 100644
index 0000000..14f7d5b
--- /dev/null
+++ b/pkg/front_end/testcases/general/extension_types_feature_not_enabled.dart.weak.transformed.expect
@@ -0,0 +1,20 @@
+library /*isNonNullableByDefault*/;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/general/extension_types_feature_not_enabled.dart:9:11: Error: This requires the 'extension-types' language feature to be enabled.
+// Try updating your pubspec.yaml to set the minimum SDK constraint to 2.14 or higher, and running 'pub get'.
+// extension type E on A {} // Error because of 'type'.
+//           ^^^^
+//
+import self as self;
+import "dart:core" as core;
+
+class A extends core::Object {
+  synthetic constructor •() → self::A
+    : super core::Object::•()
+    ;
+}
+extension type E on self::A {
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/textual_outline.status b/pkg/front_end/testcases/textual_outline.status
index 2e24e68..31ef5f3 100644
--- a/pkg/front_end/testcases/textual_outline.status
+++ b/pkg/front_end/testcases/textual_outline.status
@@ -63,6 +63,7 @@
 general/error_recovery/issue_39230.crash: FormatterCrash
 general/error_recovery/issue_39958_01: FormatterCrash
 general/error_recovery/issue_43090.crash: FormatterCrash
+general/extension_types_feature_not_enabled: FormatterCrash
 general/function_type_default_value: FormatterCrash
 general/incomplete_field_formal_parameter: FormatterCrash
 general/invalid_operator2: FormatterCrash
diff --git a/pkg/nnbd_migration/lib/instrumentation.dart b/pkg/nnbd_migration/lib/instrumentation.dart
index a5fa5ce..98be08f 100644
--- a/pkg/nnbd_migration/lib/instrumentation.dart
+++ b/pkg/nnbd_migration/lib/instrumentation.dart
@@ -43,13 +43,6 @@
         location.columnNumber, _computeElementFullName(element));
   }
 
-  CodeReference.fromJson(dynamic json)
-      : path = json['path'] as String,
-        offset = json['offset'] as int,
-        line = json['line'] as int,
-        column = json['col'] as int,
-        function = json['function'] as String;
-
   /// Gets a short description of this code reference (using the last component
   /// of the path rather than the full path)
   String get shortName => '$shortPath:$line:$column';
@@ -60,16 +53,6 @@
     return pathAsUri.pathSegments.last;
   }
 
-  Map<String, Object> toJson() {
-    return {
-      'path': path,
-      'offset': offset,
-      'line': line,
-      'col': column,
-      if (function != null) 'function': function
-    };
-  }
-
   @override
   String toString() {
     var pathAsUri = Uri.file(path);
diff --git a/pkg/nnbd_migration/lib/nullability_state.dart b/pkg/nnbd_migration/lib/nullability_state.dart
index 9323f8c..0adfd38 100644
--- a/pkg/nnbd_migration/lib/nullability_state.dart
+++ b/pkg/nnbd_migration/lib/nullability_state.dart
@@ -28,27 +28,12 @@
   /// intent (see [direct]).
   final bool isDirect;
 
-  factory NonNullIntent.fromJson(dynamic json) {
-    switch (json as String) {
-      case 'none':
-        return none;
-      case 'indirect':
-        return indirect;
-      case 'direct':
-        return direct;
-      default:
-        throw StateError('Unrecognized nullability $json');
-    }
-  }
-
   const NonNullIntent._(this.name, this.isPresent, {this.isDirect = false});
 
   /// Returns a [NonNullIntent] object representing the result of adding
   /// indirect non-null intent to `this`.
   NonNullIntent addIndirect() => isPresent ? this : indirect;
 
-  String toJson() => name;
-
   @override
   String toString() => name;
 }
@@ -83,24 +68,9 @@
   /// later be used in a contravariant way that requires it to be nullable.
   final bool isExactNullable;
 
-  factory Nullability.fromJson(dynamic json) {
-    switch (json as String) {
-      case 'non-nullable':
-        return nonNullable;
-      case 'ordinary nullable':
-        return ordinaryNullable;
-      case 'exact nullable':
-        return exactNullable;
-      default:
-        throw StateError('Unrecognized nullability $json');
-    }
-  }
-
   const Nullability._(this.name, this.isNullable,
       {this.isExactNullable = false});
 
-  String toJson() => name;
-
   @override
   String toString() => name;
 }
diff --git a/pkg/nnbd_migration/lib/src/nullability_migration_impl.dart b/pkg/nnbd_migration/lib/src/nullability_migration_impl.dart
index ca75cdf..1738fe8 100644
--- a/pkg/nnbd_migration/lib/src/nullability_migration_impl.dart
+++ b/pkg/nnbd_migration/lib/src/nullability_migration_impl.dart
@@ -5,7 +5,6 @@
 import 'package:analyzer/dart/analysis/features.dart';
 import 'package:analyzer/dart/analysis/results.dart';
 import 'package:analyzer/dart/element/element.dart';
-import 'package:analyzer/file_system/physical_file_system.dart';
 import 'package:analyzer/src/dart/analysis/session.dart';
 import 'package:analyzer/src/dart/element/type_system.dart';
 import 'package:analyzer/src/generated/source.dart';
@@ -21,16 +20,11 @@
 import 'package:nnbd_migration/src/fix_builder.dart';
 import 'package:nnbd_migration/src/node_builder.dart';
 import 'package:nnbd_migration/src/nullability_node.dart';
-import 'package:nnbd_migration/src/postmortem_file.dart';
 import 'package:nnbd_migration/src/variables.dart';
 import 'package:pub_semver/pub_semver.dart';
 
 /// Implementation of the [NullabilityMigration] public API.
 class NullabilityMigrationImpl implements NullabilityMigration {
-  /// Set this constant to a pathname to cause nullability migration to output
-  /// a post-mortem file that can be later examined by tool/postmortem.dart.
-  static const String _postmortemPath = null;
-
   final NullabilityMigrationListener listener;
 
   Variables _variables;
@@ -54,11 +48,6 @@
 
   final _decoratedTypeParameterBounds = DecoratedTypeParameterBounds();
 
-  /// If not `null`, the object that will be used to write out post-mortem
-  /// information once migration is complete.
-  final PostmortemFileWriter _postmortemFileWriter =
-      _makePostmortemFileWriter();
-
   final LineInfo Function(String) _getLineInfo;
 
   /// Map from [Source] object to a boolean indicating whether the source is
@@ -110,7 +99,6 @@
       this.warnOnWeakCode,
       this._getLineInfo) {
     _instrumentation?.immutableNodes(_graph.never, _graph.always);
-    _postmortemFileWriter?.graph = _graph;
   }
 
   @override
@@ -142,7 +130,7 @@
     ExperimentStatusException.sanityCheck(result);
     if (!_propagated) {
       _propagated = true;
-      _graph.propagate(_postmortemFileWriter);
+      _graph.propagate();
     }
     var unit = result.unit;
     var compilationUnit = unit.declaredElement;
@@ -191,7 +179,6 @@
   }
 
   Map<String, Version> finish() {
-    _postmortemFileWriter?.write();
     _instrumentation?.finished();
     return _neededPackages;
   }
@@ -212,8 +199,7 @@
         result.libraryElement.exportedLibraries);
     if (_variables == null) {
       _variables = Variables(_graph, result.typeProvider, _getLineInfo,
-          instrumentation: _instrumentation,
-          postmortemFileWriter: _postmortemFileWriter);
+          instrumentation: _instrumentation);
       _decoratedClassHierarchy = DecoratedClassHierarchy(_variables, _graph);
     }
     var unit = result.unit;
@@ -257,7 +243,7 @@
 
   @override
   void update() {
-    _graph.update(_postmortemFileWriter);
+    _graph.update();
   }
 
   /// Records the opt in/out status of all libraries in [libraries], and any
@@ -289,10 +275,4 @@
     );
     return location;
   }
-
-  static PostmortemFileWriter _makePostmortemFileWriter() {
-    if (_postmortemPath == null) return null;
-    return PostmortemFileWriter(
-        PhysicalResourceProvider.INSTANCE.getFile(_postmortemPath));
-  }
 }
diff --git a/pkg/nnbd_migration/lib/src/nullability_node.dart b/pkg/nnbd_migration/lib/src/nullability_node.dart
index 11d8725..3f35f7b 100644
--- a/pkg/nnbd_migration/lib/src/nullability_node.dart
+++ b/pkg/nnbd_migration/lib/src/nullability_node.dart
@@ -13,7 +13,6 @@
 import 'package:nnbd_migration/src/expression_checks.dart';
 import 'package:nnbd_migration/src/hint_action.dart';
 import 'package:nnbd_migration/src/nullability_node_target.dart';
-import 'package:nnbd_migration/src/postmortem_file.dart';
 
 import 'edge_origin.dart';
 
@@ -34,32 +33,14 @@
 
   DownstreamPropagationStep();
 
-  DownstreamPropagationStep.fromJson(
-      dynamic json, NullabilityGraphDeserializer deserializer)
-      : targetNode = deserializer.nodeForId(json['target'] as int)
-            as NullabilityNodeMutable,
-        newState = Nullability.fromJson(json['newState']);
-
   @override
   DownstreamPropagationStep get principalCause;
-
-  @override
-  Map<String, Object> toJson(NullabilityGraphSerializer serializer) {
-    return {
-      'target': serializer.idForNode(targetNode),
-      'newState': newState.toJson()
-    };
-  }
 }
 
 /// Base class for steps that occur as part of propagating exact nullability
 /// upstream through the nullability graph.
 abstract class ExactNullablePropagationStep extends DownstreamPropagationStep {
   ExactNullablePropagationStep();
-
-  ExactNullablePropagationStep.fromJson(
-      dynamic json, NullabilityGraphDeserializer deserializer)
-      : super.fromJson(json, deserializer);
 }
 
 /// Conditions of the "lateness" of a [NullabilityNode].
@@ -109,23 +90,6 @@
   /// `setUp` function.
   final bool isSetupAssignment;
 
-  NullabilityEdge.fromJson(
-      dynamic json, NullabilityGraphDeserializer deserializer)
-      : destinationNode = deserializer.nodeForId(json['dest'] as int),
-        upstreamNodes = [],
-        _kind = _deserializeKind(json['kind']),
-        codeReference =
-            json['code'] == null ? null : CodeReference.fromJson(json['code']),
-        description = json['description'] as String,
-        isUninit = json['isUninit'] as bool,
-        isSetupAssignment = json['isSetupAssignment'] as bool {
-    deserializer.defer(() {
-      for (var id in json['us'] as List<dynamic>) {
-        upstreamNodes.add(deserializer.nodeForId(id as int));
-      }
-    });
-  }
-
   NullabilityEdge._(
       this.destinationNode, this.upstreamNodes, this._kind, this.description,
       {this.codeReference, this.isUninit, this.isSetupAssignment});
@@ -171,36 +135,6 @@
   @override
   NullabilityNode get sourceNode => upstreamNodes.first;
 
-  Map<String, Object> toJson(NullabilityGraphSerializer serializer) {
-    var json = <String, Object>{};
-    switch (_kind) {
-      case _NullabilityEdgeKind.soft:
-        break;
-      case _NullabilityEdgeKind.uncheckable:
-        json['kind'] = 'uncheckable';
-        break;
-      case _NullabilityEdgeKind.uncheckableHard:
-        json['kind'] = 'uncheckableHard';
-        break;
-      case _NullabilityEdgeKind.hard:
-        json['kind'] = 'hard';
-        break;
-      case _NullabilityEdgeKind.union:
-        json['kind'] = 'union';
-        break;
-      case _NullabilityEdgeKind.dummy:
-        json['kind'] = 'dummy';
-        break;
-    }
-    if (codeReference != null) json['code'] = codeReference.toJson();
-    if (description != null) json['description'] = description;
-    serializer.defer(() {
-      json['dest'] = serializer.idForNode(destinationNode);
-      json['us'] = [for (var n in upstreamNodes) serializer.idForNode(n)];
-    });
-    return json;
-  }
-
   @override
   String toString({NodeToIdMapper idMapper}) {
     var edgeDecorations = <Object>[];
@@ -229,23 +163,6 @@
     return '${sourceNode.toString(idMapper: idMapper)} $edgeDecoration-> '
         '${destinationNode.toString(idMapper: idMapper)}';
   }
-
-  static _NullabilityEdgeKind _deserializeKind(dynamic json) {
-    if (json == null) return _NullabilityEdgeKind.soft;
-    var kind = json as String;
-    switch (kind) {
-      case 'uncheckable':
-        return _NullabilityEdgeKind.uncheckable;
-      case 'uncheckableHard':
-        return _NullabilityEdgeKind.uncheckableHard;
-      case 'hard':
-        return _NullabilityEdgeKind.hard;
-      case 'union':
-        return _NullabilityEdgeKind.union;
-      default:
-        throw StateError('Unrecognized edge kind $kind');
-    }
-  }
 }
 
 /// Data structure to keep track of the relationship between [NullabilityNode]
@@ -286,18 +203,6 @@
       : always = _NullabilityNodeImmutable('always', true),
         never = _NullabilityNodeImmutable('never', false);
 
-  NullabilityGraph.fromJson(
-      dynamic json, NullabilityGraphDeserializer deserializer)
-      : instrumentation = null,
-        always = deserializer.nodeForId(json['always'] as int),
-        never = deserializer.nodeForId(json['never'] as int) {
-    var serializedNodes = json['nodes'] as List<dynamic>;
-    for (int id = 0; id < serializedNodes.length; id++) {
-      nodes.add(deserializer.nodeForId(id));
-    }
-    deserializer.finish();
-  }
-
   /// Records that [sourceNode] is immediately upstream from [destinationNode].
   ///
   /// Returns the edge created by the connection.
@@ -433,25 +338,14 @@
 
   /// Determines the nullability of each node in the graph by propagating
   /// nullability information from one node to another.
-  PropagationResult propagate(PostmortemFileWriter postmortemFileWriter) {
-    postmortemFileWriter?.clearPropagationSteps();
+  PropagationResult propagate() {
     if (_debugBeforePropagation) debugDump();
     var propagationState =
-        _PropagationState(always, never, postmortemFileWriter).result;
+        _PropagationState(always, never).result;
     if (_debugAfterPropagation) debugDump();
     return propagationState;
   }
 
-  Map<String, Object> toJson(NullabilityGraphSerializer serializer) {
-    var json = <String, Object>{};
-    json['always'] = serializer.idForNode(always);
-    json['never'] = serializer.idForNode(never);
-    serializer.finish();
-    json['nodes'] = serializer.serializedNodes;
-    json['edges'] = serializer.serializedEdges;
-    return json;
-  }
-
   /// Records that nodes [x] and [y] should have exactly the same nullability.
   void union(NullabilityNode x, NullabilityNode y, EdgeOrigin origin) {
     _connect([x], y, _NullabilityEdgeKind.union, origin);
@@ -459,7 +353,7 @@
   }
 
   /// Update the graph after an edge has been added or removed.
-  void update(PostmortemFileWriter postmortemFileWriter) {
+  void update() {
     //
     // Reset the state of the nodes.
     //
@@ -477,7 +371,7 @@
     //
     // Re-run the propagation step.
     //
-    propagate(postmortemFileWriter);
+    propagate();
   }
 
   NullabilityEdge _connect(
@@ -515,96 +409,6 @@
   }
 }
 
-/// Helper object used to deserialize a nullability graph from a JSON
-/// representation.
-class NullabilityGraphDeserializer implements NodeToIdMapper {
-  final List<dynamic> _serializedNodes;
-
-  final List<dynamic> _serializedEdges;
-
-  final Map<int, NullabilityNode> _idToNodeMap = {};
-
-  final Map<int, NullabilityEdge> _idToEdgeMap = {};
-
-  final List<void Function()> _deferred = [];
-
-  final Map<NullabilityNode, int> _nodeToIdMap = {};
-
-  final Map<PropagationStep, int> _stepToIdMap = {};
-
-  final List<PropagationStep> _propagationSteps;
-
-  NullabilityGraphDeserializer(
-      this._serializedNodes, this._serializedEdges, this._propagationSteps);
-
-  /// Defers a deserialization action until later.  The nullability node
-  /// `fromJson` constructors use this method to defer populating edge lists
-  /// until all nodes have been deserialized.
-  void defer(void Function() callback) {
-    _deferred.add(callback);
-  }
-
-  /// Gets the edge having the given [id], deserializing it if it hasn't been
-  /// deserialized already.
-  NullabilityEdge edgeForId(int id) {
-    var edge = _idToEdgeMap[id];
-    if (edge == null) {
-      _idToEdgeMap[id] =
-          edge = NullabilityEdge.fromJson(_serializedEdges[id], this);
-    }
-    return edge;
-  }
-
-  /// Runs all deferred actions that have been passed to [defer].
-  void finish() {
-    while (_deferred.isNotEmpty) {
-      var callback = _deferred.removeLast();
-      callback();
-    }
-  }
-
-  @override
-  int idForNode(NullabilityNodeInfo node) => _nodeToIdMap[node];
-
-  /// Gets the node having the given [id], deserializing it if it hasn't been
-  /// deserialized already.
-  NullabilityNode nodeForId(int id) {
-    var node = _idToNodeMap[id];
-    if (node == null) {
-      _idToNodeMap[id] = node = _deserializeNode(id);
-      _nodeToIdMap[node] = id;
-    }
-    return node;
-  }
-
-  /// Records that the given [step] was stored in the postmortem file with the
-  /// given [id] number.
-  void recordStepId(PropagationStep step, int id) {
-    _stepToIdMap[step] = id;
-  }
-
-  /// Gets the propagation step having the given [id].
-  PropagationStep stepForId(int id) =>
-      id == null ? null : _propagationSteps[id];
-
-  NullabilityNode _deserializeNode(int id) {
-    var json = _serializedNodes[id];
-    var kind = json['kind'] as String;
-    switch (kind) {
-      case 'immutable':
-        return _NullabilityNodeImmutable.fromJson(json, this);
-      case 'simple':
-        return _NullabilityNodeSimple.fromJson(json, this);
-      case 'lub':
-        return NullabilityNodeForLUB.fromJson(json, this);
-      case 'substitution':
-        return NullabilityNodeForSubstitution.fromJson(json, this);
-      default:
-        throw StateError('Unrecognized node kind $kind');
-    }
-  }
-}
-
 /// Same as [NullabilityGraph], but extended with extra methods for easier
 /// testing.
 @visibleForTesting
@@ -636,81 +440,6 @@
   }
 }
 
-/// Helper object used to serialize a nullability graph into a JSON
-/// representation.
-class NullabilityGraphSerializer {
-  /// The list of serialized node objects to be stored in the output JSON.
-  final List<Map<String, Object>> serializedNodes = [];
-
-  final Map<NullabilityNode, int> _nodeToIdMap = {};
-
-  /// The list of serialized edge objects to be stored in the output JSON.
-  final List<Map<String, Object>> serializedEdges = [];
-
-  final Map<NullabilityEdge, int> _edgeToIdMap = {};
-
-  final List<void Function()> _deferred = [];
-
-  bool _serializingNodeOrEdge = false;
-
-  final Map<PropagationStep, int> _stepToIdMap = {};
-
-  /// Defers a serialization action until later.  The nullability node
-  /// `toJson` methods use this method to defer serializing edge lists
-  /// until all nodes have been serialized.
-  void defer(void Function() callback) {
-    _deferred.add(callback);
-  }
-
-  /// Runs all deferred actions that have been passed to [defer].
-  void finish() {
-    while (_deferred.isNotEmpty) {
-      var callback = _deferred.removeLast();
-      callback();
-    }
-  }
-
-  /// Gets the id for the given [edge], serializing it if it hasn't been
-  /// serialized already.
-  int idForEdge(NullabilityEdge edge) {
-    var result = _edgeToIdMap[edge];
-    if (result == null) {
-      if (_serializingNodeOrEdge) {
-        throw StateError('Illegal nesting of idForEdge');
-      }
-      _serializingNodeOrEdge = true;
-      assert(_edgeToIdMap.length == serializedEdges.length);
-      result = _edgeToIdMap[edge] = _edgeToIdMap.length;
-      serializedEdges.add(edge.toJson(this));
-      _serializingNodeOrEdge = false;
-    }
-    return result;
-  }
-
-  /// Gets the id for the given [node], serializing it if it hasn't been
-  /// serialized already.
-  int idForNode(NullabilityNode node) {
-    var result = _nodeToIdMap[node];
-    if (result == null) {
-      if (_serializingNodeOrEdge) {
-        throw StateError('Illegal nesting of idForEdge');
-      }
-      _serializingNodeOrEdge = true;
-      assert(_nodeToIdMap.length == serializedNodes.length);
-      result = _nodeToIdMap[node] = _nodeToIdMap.length;
-      serializedNodes.add(node.toJson(this));
-      _serializingNodeOrEdge = false;
-    }
-    return result;
-  }
-
-  int idForStep(PropagationStep step) => _stepToIdMap[step];
-
-  void recordStepId(PropagationStep step, int id) {
-    _stepToIdMap[step] = id;
-  }
-}
-
 /// Representation of a single node in the nullability inference graph.
 ///
 /// Initially, this is just a wrapper over constraint variables, and the
@@ -785,25 +514,6 @@
   factory NullabilityNode.forTypeAnnotation(NullabilityNodeTarget target) =>
       _NullabilityNodeSimple(target);
 
-  NullabilityNode.fromJson(
-      dynamic json, NullabilityGraphDeserializer deserializer) {
-    deserializer.defer(() {
-      if (json['isPossiblyOptional'] == true) {
-        _isPossiblyOptional = true;
-      }
-      for (var id in json['ds'] ?? []) {
-        _downstreamEdges.add(deserializer.edgeForId(id as int));
-      }
-      for (var id in json['us'] ?? []) {
-        _upstreamEdges.add(deserializer.edgeForId(id as int));
-      }
-      for (var id in json['outerCompoundNodes'] ?? []) {
-        outerCompoundNodes
-            .add(deserializer.nodeForId(id as int) as NullabilityNodeCompound);
-      }
-    });
-  }
-
   NullabilityNode._();
 
   @override
@@ -847,8 +557,6 @@
   @override
   UpstreamPropagationStep get whyNotNullable;
 
-  String get _jsonKind;
-
   Nullability get _nullability;
 
   /// Records the fact that an invocation was made to a function with named
@@ -864,28 +572,6 @@
   /// Reset the state of this node to what it was before the graph was solved.
   void resetState();
 
-  Map<String, Object> toJson(NullabilityGraphSerializer serializer) {
-    var json = <String, Object>{};
-    json['kind'] = _jsonKind;
-    if (_isPossiblyOptional) {
-      json['isPossiblyOptional'] = true;
-    }
-    serializer.defer(() {
-      if (_downstreamEdges.isNotEmpty) {
-        json['ds'] = [for (var e in _downstreamEdges) serializer.idForEdge(e)];
-      }
-      if (_upstreamEdges.isNotEmpty) {
-        json['us'] = [for (var e in _upstreamEdges) serializer.idForEdge(e)];
-      }
-      if (outerCompoundNodes.isNotEmpty) {
-        json['outerCompoundNodes'] = [
-          for (var e in outerCompoundNodes) serializer.idForNode(e)
-        ];
-      }
-    });
-    return json;
-  }
-
   String toString({NodeToIdMapper idMapper}) {
     var name = displayName;
     if (idMapper == null) {
@@ -909,10 +595,6 @@
 abstract class NullabilityNodeCompound extends NullabilityNodeMutable {
   NullabilityNodeCompound() : super._();
 
-  NullabilityNodeCompound.fromJson(
-      dynamic json, NullabilityGraphDeserializer deserializer)
-      : super.fromJson(json, deserializer);
-
   /// A map describing each of the node's components by name.
   Map<String, NullabilityNode> get componentsByName;
 
@@ -932,12 +614,6 @@
 
   final NullabilityNode right;
 
-  NullabilityNodeForLUB.fromJson(
-      dynamic json, NullabilityGraphDeserializer deserializer)
-      : left = deserializer.nodeForId(json['left'] as int),
-        right = deserializer.nodeForId(json['right'] as int),
-        super.fromJson(json, deserializer);
-
   NullabilityNodeForLUB._(this.left, this.right) {
     left.outerCompoundNodes.add(this);
     right.outerCompoundNodes.add(this);
@@ -954,23 +630,10 @@
   Iterable<NullabilityNode> get _components => [left, right];
 
   @override
-  String get _jsonKind => 'lub';
-
-  @override
   void resetState() {
     left.resetState();
     right.resetState();
   }
-
-  @override
-  Map<String, Object> toJson(NullabilityGraphSerializer serializer) {
-    var json = super.toJson(serializer);
-    serializer.defer(() {
-      json['left'] = serializer.idForNode(left);
-      json['right'] = serializer.idForNode(right);
-    });
-    return json;
-  }
 }
 
 /// Derived class for nullability nodes that arise from type variable
@@ -983,12 +646,6 @@
   @override
   final NullabilityNode outerNode;
 
-  NullabilityNodeForSubstitution.fromJson(
-      dynamic json, NullabilityGraphDeserializer deserializer)
-      : innerNode = deserializer.nodeForId(json['inner'] as int),
-        outerNode = deserializer.nodeForId(json['outer'] as int),
-        super.fromJson(json, deserializer);
-
   NullabilityNodeForSubstitution._(this.innerNode, this.outerNode) {
     innerNode.outerCompoundNodes.add(this);
     outerNode.outerCompoundNodes.add(this);
@@ -1006,23 +663,10 @@
   Iterable<NullabilityNode> get _components => [innerNode, outerNode];
 
   @override
-  String get _jsonKind => 'substitution';
-
-  @override
   void resetState() {
     innerNode.resetState();
     outerNode.resetState();
   }
-
-  @override
-  Map<String, Object> toJson(NullabilityGraphSerializer serializer) {
-    var json = super.toJson(serializer);
-    serializer.defer(() {
-      json['inner'] = serializer.idForNode(innerNode);
-      json['outer'] = serializer.idForNode(outerNode);
-    });
-    return json;
-  }
 }
 
 /// Base class for nullability nodes whose state can be mutated safely.
@@ -1038,16 +682,6 @@
 
   UpstreamPropagationStep _whyNotNullable;
 
-  NullabilityNodeMutable.fromJson(
-      dynamic json, NullabilityGraphDeserializer deserializer)
-      : _nullability = json['nullability'] == null
-            ? Nullability.nonNullable
-            : Nullability.fromJson(json['nullability']),
-        _nonNullIntent = json['nonNullIntent'] == null
-            ? NonNullIntent.none
-            : NonNullIntent.fromJson(json['nonNullIntent']),
-        super.fromJson(json, deserializer);
-
   NullabilityNodeMutable._(
       {Nullability initialNullability = Nullability.nonNullable})
       : _nullability = initialNullability,
@@ -1078,18 +712,6 @@
     _nonNullIntent = NonNullIntent.none;
     _whyNullable = null;
   }
-
-  @override
-  Map<String, Object> toJson(NullabilityGraphSerializer serializer) {
-    var json = super.toJson(serializer);
-    if (_nullability != Nullability.nonNullable) {
-      json['nullability'] = _nullability.toJson();
-    }
-    if (_nonNullIntent != NonNullIntent.none) {
-      json['intent'] = _nonNullIntent.toJson();
-    }
-    return json;
-  }
 }
 
 /// Information produced by [NullabilityGraph.propagate] about the results of
@@ -1108,23 +730,6 @@
 abstract class PropagationStep implements PropagationStepInfo {
   PropagationStep();
 
-  factory PropagationStep.fromJson(
-      json, NullabilityGraphDeserializer deserializer) {
-    var kind = json['kind'] as String;
-    switch (kind) {
-      case 'downstream':
-        return SimpleDownstreamPropagationStep.fromJson(json, deserializer);
-      case 'exact':
-        return SimpleExactNullablePropagationStep.fromJson(json, deserializer);
-      case 'resolveSubstitution':
-        return ResolveSubstitutionPropagationStep.fromJson(json, deserializer);
-      case 'upstream':
-        return UpstreamPropagationStep.fromJson(json, deserializer);
-      default:
-        throw StateError('Unrecognized propagation step kind: $kind');
-    }
-  }
-
   /// The location in the source code that caused this step to be necessary,
   /// or `null` if not known.
   CodeReference get codeReference => null;
@@ -1133,8 +738,6 @@
   /// no previous step.
   PropagationStep get principalCause;
 
-  Map<String, Object> toJson(NullabilityGraphSerializer serializer);
-
   @override
   String toString({NodeToIdMapper idMapper});
 }
@@ -1151,27 +754,10 @@
 
   ResolveSubstitutionPropagationStep(this.principalCause, this.node);
 
-  ResolveSubstitutionPropagationStep.fromJson(
-      dynamic json, NullabilityGraphDeserializer deserializer)
-      : principalCause = deserializer.stepForId(json['cause'] as int)
-            as DownstreamPropagationStep,
-        node = deserializer.nodeForId(json['node'] as int)
-            as NullabilityNodeForSubstitution,
-        super.fromJson(json, deserializer);
-
   @override
   EdgeInfo get edge => null;
 
   @override
-  Map<String, Object> toJson(NullabilityGraphSerializer serializer) {
-    var json = super.toJson(serializer);
-    json['kind'] = 'resolveSubstitution';
-    json['cause'] = serializer.idForStep(principalCause);
-    json['node'] = serializer.idForNode(node);
-    return json;
-  }
-
-  @override
   String toString({NodeToIdMapper idMapper}) =>
       '${targetNode.toString(idMapper: idMapper)} becomes $newState due to '
       '${node.toString(idMapper: idMapper)}';
@@ -1188,26 +774,10 @@
 
   SimpleDownstreamPropagationStep(this.principalCause, this.edge);
 
-  SimpleDownstreamPropagationStep.fromJson(
-      dynamic json, NullabilityGraphDeserializer deserializer)
-      : principalCause = deserializer.stepForId(json['cause'] as int)
-            as DownstreamPropagationStep,
-        edge = deserializer.edgeForId(json['edge'] as int),
-        super.fromJson(json, deserializer);
-
   @override
   CodeReference get codeReference => edge.codeReference;
 
   @override
-  Map<String, Object> toJson(NullabilityGraphSerializer serializer) {
-    var json = super.toJson(serializer);
-    json['kind'] = 'downstream';
-    json['cause'] = serializer.idForStep(principalCause);
-    json['edge'] = serializer.idForEdge(edge);
-    return json;
-  }
-
-  @override
   String toString({NodeToIdMapper idMapper}) =>
       '${targetNode.toString(idMapper: idMapper)} becomes $newState due to '
       '${edge.toString(idMapper: idMapper)}';
@@ -1224,22 +794,6 @@
 
   SimpleExactNullablePropagationStep(this.principalCause, this.edge);
 
-  SimpleExactNullablePropagationStep.fromJson(
-      dynamic json, NullabilityGraphDeserializer deserializer)
-      : principalCause = deserializer.stepForId(json['cause'] as int)
-            as ExactNullablePropagationStep,
-        edge = deserializer.edgeForId(json['edge'] as int),
-        super.fromJson(json, deserializer);
-
-  @override
-  Map<String, Object> toJson(NullabilityGraphSerializer serializer) {
-    var json = super.toJson(serializer);
-    json['kind'] = 'exact';
-    json['cause'] = serializer.idForStep(principalCause);
-    json['edge'] = serializer.idForEdge(edge);
-    return json;
-  }
-
   @override
   String toString({NodeToIdMapper idMapper}) =>
       '${targetNode.toString(idMapper: idMapper)} becomes $newState due to '
@@ -1270,31 +824,10 @@
       this.principalCause, this.node, this.newNonNullIntent, this.edge,
       {this.isStartingPoint = false});
 
-  UpstreamPropagationStep.fromJson(
-      dynamic json, NullabilityGraphDeserializer deserializer)
-      : principalCause = deserializer.stepForId(json['cause'] as int)
-            as UpstreamPropagationStep,
-        node = deserializer.nodeForId(json['node'] as int),
-        newNonNullIntent = NonNullIntent.fromJson(json['newState']),
-        edge = deserializer.edgeForId(json['edge'] as int),
-        isStartingPoint = json['isStartingPoint'] as bool ?? false;
-
   @override
   CodeReference get codeReference => edge?.codeReference;
 
   @override
-  Map<String, Object> toJson(NullabilityGraphSerializer serializer) {
-    return {
-      'kind': 'upstream',
-      'cause': serializer.idForStep(principalCause),
-      'node': serializer.idForNode(node),
-      'newState': newNonNullIntent.toJson(),
-      'edge': serializer.idForEdge(edge),
-      if (isStartingPoint) 'isStartingPoint': true
-    };
-  }
-
-  @override
   String toString({NodeToIdMapper idMapper}) =>
       '${node.toString(idMapper: idMapper)} becomes $newNonNullIntent';
 }
@@ -1336,12 +869,6 @@
 
   _NullabilityNodeImmutable(this.displayName, this.isNullable) : super._();
 
-  _NullabilityNodeImmutable.fromJson(
-      dynamic json, NullabilityGraphDeserializer deserializer)
-      : displayName = json['displayName'] as String,
-        isNullable = json['isNullable'] as bool,
-        super.fromJson(json, deserializer);
-
   @override
   String get debugSuffix => isNullable ? '?' : '';
 
@@ -1369,9 +896,6 @@
   DownstreamPropagationStepInfo get whyNullable => null;
 
   @override
-  String get _jsonKind => 'immutable';
-
-  @override
   Nullability get _nullability =>
       isNullable ? Nullability.ordinaryNullable : Nullability.nonNullable;
 
@@ -1379,14 +903,6 @@
   void resetState() {
     // There is no state to reset.
   }
-
-  @override
-  Map<String, Object> toJson(NullabilityGraphSerializer serializer) {
-    var json = super.toJson(serializer);
-    json['displayName'] = displayName;
-    json['isNullable'] = isNullable;
-    return json;
-  }
 }
 
 class _NullabilityNodeSimple extends NullabilityNodeMutable {
@@ -1394,27 +910,11 @@
 
   _NullabilityNodeSimple(this.target) : super._();
 
-  _NullabilityNodeSimple.fromJson(
-      dynamic json, NullabilityGraphDeserializer deserializer)
-      : target =
-            NullabilityNodeTarget.text(json['targetDisplayName'] as String),
-        super.fromJson(json, deserializer);
-
   @override
   CodeReference get codeReference => target.codeReference;
 
   @override
   String get displayName => target.displayName;
-
-  @override
-  String get _jsonKind => 'simple';
-
-  @override
-  Map<String, Object> toJson(NullabilityGraphSerializer serializer) {
-    var json = super.toJson(serializer);
-    json['targetDisplayName'] = target.displayName;
-    return json;
-  }
 }
 
 /// Workspace for performing graph propagation.
@@ -1437,13 +937,11 @@
   final Queue<SimpleDownstreamPropagationStep> _pendingDownstreamSteps =
       Queue();
 
-  final PostmortemFileWriter _postmortemFileWriter;
-
   /// During execution of [_propagateDownstream], a list of all the substitution
   /// nodes that have not yet been resolved.
   List<ResolveSubstitutionPropagationStep> _pendingSubstitutions = [];
 
-  _PropagationState(this._always, this._never, this._postmortemFileWriter) {
+  _PropagationState(this._always, this._never) {
     _propagateUpstream();
     _propagateDownstream();
   }
@@ -1651,7 +1149,6 @@
     var newNonNullIntent = step.newNonNullIntent;
     var oldNonNullIntent = node.nonNullIntent;
     node._nonNullIntent = newNonNullIntent;
-    _postmortemFileWriter?.addPropagationStep(step);
     if (!oldNonNullIntent.isPresent) {
       node._whyNotNullable = step;
     }
@@ -1662,7 +1159,6 @@
     var newState = step.newState;
     var oldState = node._nullability;
     node._nullability = newState;
-    _postmortemFileWriter?.addPropagationStep(step);
     if (!oldState.isNullable) {
       node._whyNullable = step;
       // Was not previously nullable, so we need to propagate.
diff --git a/pkg/nnbd_migration/lib/src/postmortem_file.dart b/pkg/nnbd_migration/lib/src/postmortem_file.dart
deleted file mode 100644
index ae696fe..0000000
--- a/pkg/nnbd_migration/lib/src/postmortem_file.dart
+++ /dev/null
@@ -1,130 +0,0 @@
-// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-import 'dart:convert' as convert;
-
-import 'package:analyzer/file_system/file_system.dart';
-import 'package:nnbd_migration/instrumentation.dart';
-import 'package:nnbd_migration/src/decorated_type.dart';
-import 'package:nnbd_migration/src/nullability_node.dart';
-import 'package:nnbd_migration/src/variables.dart';
-
-/// Helper class for reading a postmortem file.
-class PostmortemFileReader {
-  final NullabilityGraphDeserializer deserializer;
-
-  final NullabilityGraph graph;
-
-  final List<PropagationStep> propagationSteps;
-
-  final Map<String, Map<int, Map<String, NullabilityNode>>> fileDecorations;
-
-  factory PostmortemFileReader.read(File file) {
-    var json = convert.json.decode(file.readAsStringSync());
-    List<PropagationStep> deserializedSteps = [];
-    var deserializer = NullabilityGraphDeserializer(
-        json['graph']['nodes'] as List<dynamic>,
-        json['graph']['edges'] as List<dynamic>,
-        deserializedSteps);
-    return PostmortemFileReader._(json, deserializer, deserializedSteps);
-  }
-
-  PostmortemFileReader._(dynamic json, this.deserializer, this.propagationSteps)
-      : graph = NullabilityGraph.fromJson(json['graph'], deserializer),
-        fileDecorations = {
-          for (var fileEntry
-              in (json['fileDecorations'] as Map<String, dynamic>).entries)
-            fileEntry.key: {
-              for (var decorationEntry
-                  in (fileEntry.value as Map<String, dynamic>).entries)
-                int.parse(decorationEntry.key): {
-                  for (var roleEntry
-                      in (decorationEntry.value as Map<String, dynamic>)
-                          .entries)
-                    roleEntry.key:
-                        deserializer.nodeForId(roleEntry.value as int)
-                }
-            }
-        } {
-    _decodePropagationSteps(json['propagationSteps']);
-  }
-
-  NodeToIdMapper get idMapper => deserializer;
-
-  void findDecorationsByNode(NullabilityNode node,
-      void Function(String path, OffsetEndPair span, String role) callback) {
-    for (var fileEntry in fileDecorations.entries) {
-      for (var decorationEntry in fileEntry.value.entries) {
-        for (var roleEntry in decorationEntry.value.entries) {
-          if (identical(roleEntry.value, node)) {
-            callback(
-                fileEntry.key,
-                Variables.spanForUniqueIdentifier(decorationEntry.key),
-                roleEntry.key);
-          }
-        }
-      }
-    }
-  }
-
-  void _decodePropagationSteps(dynamic json) {
-    for (var serializedStep in json) {
-      var step = PropagationStep.fromJson(serializedStep, deserializer);
-      deserializer.recordStepId(step, propagationSteps.length);
-      propagationSteps.add(step);
-    }
-  }
-}
-
-/// Helper class for writing to a postmortem file.
-class PostmortemFileWriter {
-  final File file;
-
-  NullabilityGraph graph;
-
-  final List<PropagationStep> _propagationSteps = [];
-
-  final Map<String, Map<int, Map<String, NullabilityNode>>> _fileDecorations =
-      {};
-
-  PostmortemFileWriter(this.file);
-
-  void addPropagationStep(PropagationStep step) {
-    _propagationSteps.add(step);
-  }
-
-  void clearPropagationSteps() {
-    _propagationSteps.clear();
-  }
-
-  void storeFileDecorations(
-      String path, int location, DecoratedType decoratedType) {
-    var roles = <String, NullabilityNode>{};
-    decoratedType.recordRoles(roles);
-    (_fileDecorations[path] ??= {})[location] = roles;
-  }
-
-  void write() {
-    var json = <String, Object>{};
-    var serializer = NullabilityGraphSerializer();
-    json['graph'] = graph.toJson(serializer);
-    List<Object> serializedPropagationSteps = [];
-    for (var step in _propagationSteps) {
-      serializer.recordStepId(step, serializedPropagationSteps.length);
-      serializedPropagationSteps.add(step.toJson(serializer));
-    }
-    json['propagationSteps'] = serializedPropagationSteps;
-    json['fileDecorations'] = {
-      for (var fileEntry in _fileDecorations.entries)
-        fileEntry.key: {
-          for (var decorationEntry in (fileEntry.value).entries)
-            decorationEntry.key.toString(): {
-              for (var roleEntry in (decorationEntry.value).entries)
-                roleEntry.key: serializer.idForNode(roleEntry.value)
-            }
-        }
-    };
-    file.writeAsStringSync(convert.json.encode(json));
-  }
-}
diff --git a/pkg/nnbd_migration/lib/src/variables.dart b/pkg/nnbd_migration/lib/src/variables.dart
index 68492a3..d873271 100644
--- a/pkg/nnbd_migration/lib/src/variables.dart
+++ b/pkg/nnbd_migration/lib/src/variables.dart
@@ -24,7 +24,6 @@
 import 'package:nnbd_migration/src/fix_builder.dart';
 import 'package:nnbd_migration/src/nullability_node.dart';
 import 'package:nnbd_migration/src/nullability_node_target.dart';
-import 'package:nnbd_migration/src/postmortem_file.dart';
 import 'package:nnbd_migration/src/utilities/hint_utils.dart';
 
 /// Data structure used by [Variables.spanForUniqueIdentifier] to return an
@@ -70,12 +69,10 @@
 
   final NullabilityMigrationInstrumentation /*?*/ instrumentation;
 
-  final PostmortemFileWriter postmortemFileWriter;
-
   final LineInfo Function(String) _getLineInfo;
 
   Variables(this._graph, this._typeProvider, this._getLineInfo,
-      {this.instrumentation, this.postmortemFileWriter})
+      {this.instrumentation})
       : _alreadyMigratedCodeDecorator =
             AlreadyMigratedCodeDecorator(_graph, _typeProvider, _getLineInfo);
 
@@ -241,7 +238,6 @@
     instrumentation?.explicitTypeNullability(source, node, type.node);
     var id = uniqueIdentifierForSpan(node.offset, node.end);
     (_decoratedTypeAnnotations[source] ??= {})[id] = type;
-    postmortemFileWriter?.storeFileDecorations(source.fullName, id, type);
   }
 
   /// Associates a set of nullability checks with the given expression [node].
diff --git a/pkg/nnbd_migration/test/fix_builder_test.dart b/pkg/nnbd_migration/test/fix_builder_test.dart
index 1c437ba..c3a53aa 100644
--- a/pkg/nnbd_migration/test/fix_builder_test.dart
+++ b/pkg/nnbd_migration/test/fix_builder_test.dart
@@ -115,7 +115,7 @@
   @override
   Future<CompilationUnit> analyze(String code) async {
     var unit = await super.analyze(code);
-    graph.propagate(null);
+    graph.propagate();
     return unit;
   }
 
diff --git a/pkg/nnbd_migration/test/nullability_node_test.dart b/pkg/nnbd_migration/test/nullability_node_test.dart
index c50614f..7f46e17 100644
--- a/pkg/nnbd_migration/test/nullability_node_test.dart
+++ b/pkg/nnbd_migration/test/nullability_node_test.dart
@@ -48,7 +48,7 @@
       NullabilityNode.forTypeAnnotation(NullabilityNodeTarget.text('node $id'));
 
   void propagate() {
-    var propagationResult = graph.propagate(null);
+    var propagationResult = graph.propagate();
     unsatisfiedEdges = propagationResult.unsatisfiedEdges;
     unsatisfiedSubstitutions = propagationResult.unsatisfiedSubstitutions;
   }
diff --git a/pkg/nnbd_migration/tool/postmortem.dart b/pkg/nnbd_migration/tool/postmortem.dart
index 0584991..a523b3a 100644
--- a/pkg/nnbd_migration/tool/postmortem.dart
+++ b/pkg/nnbd_migration/tool/postmortem.dart
@@ -2,197 +2,10 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-import 'dart:io';
-
-import 'package:analyzer/file_system/physical_file_system.dart';
 import 'package:args/args.dart';
 import 'package:meta/meta.dart';
-import 'package:nnbd_migration/src/nullability_node.dart';
-import 'package:nnbd_migration/src/postmortem_file.dart';
-import 'package:nnbd_migration/src/variables.dart';
 
-void main(List<String> args) {
-  ArgParser argParser = ArgParser();
-  ArgResults parsedArgs;
-
-  argParser.addOption('file',
-      abbr: 'f', help: 'The postmortem file to analyze');
-
-  argParser.addFlag('help',
-      abbr: 'h', negatable: false, help: 'Print usage info');
-
-  List<Subcommand> subcommands = [
-    Subcommand(
-        name: 'dot', help: 'Output graph as dot file', argParser: ArgParser()),
-    Subcommand(name: 'help', help: 'Print usage info', argParser: ArgParser()),
-    Subcommand(
-        name: 'steps', help: 'Print propagation steps', argParser: ArgParser()),
-    Subcommand(
-        name: 'files', help: 'Print input file paths', argParser: ArgParser()),
-    Subcommand(
-        name: 'decorations',
-        suffix: '<path>',
-        help: 'Print decorations for a file',
-        argParser: ArgParser()),
-    Subcommand(
-      name: 'node',
-      suffix: '<id>',
-      help: 'Print details about a node',
-      argParser: ArgParser(),
-    ),
-    Subcommand(
-      name: 'trace',
-      suffix: '<id>',
-      help: 'Print a trace of why a node was made nullable/non-nullable',
-      argParser: ArgParser(),
-    ),
-    Subcommand(
-      name: 'trace_lengths',
-      help: 'Print the lengths of all traces',
-      argParser: ArgParser(),
-    ),
-  ];
-
-  for (var subcommand in subcommands) {
-    argParser.addCommand(subcommand.name, subcommand.argParser);
-  }
-
-  try {
-    parsedArgs = argParser.parse(args);
-  } on ArgParserException {
-    stderr.writeln(argParser.usage);
-    exit(1);
-  }
-  var command = parsedArgs.command;
-  if (parsedArgs['help'] as bool || command == null || command.name == 'help') {
-    print(argParser.usage);
-    for (var subcommand in subcommands) {
-      print('');
-      var suffix = subcommand.suffix == null ? '' : ' ${subcommand.suffix}';
-      print('Subcommand ${subcommand.name}$suffix: ${subcommand.help}');
-      print(subcommand.argParser.usage);
-    }
-    exit(0);
-  }
-  var filePath = parsedArgs['file'] as String;
-  if (filePath == null) {
-    print('Must specify a file to analyze using -f');
-    exit(1);
-  }
-  var reader = PostmortemFileReader.read(
-      PhysicalResourceProvider.INSTANCE.getFile(filePath));
-  switch (command.name) {
-    case 'dot':
-      reader.graph.debugDump();
-      break;
-    case 'steps':
-      for (var step in reader.propagationSteps) {
-        print(step.toString(idMapper: reader.idMapper));
-      }
-      break;
-    case 'trace':
-      var nodes = command.rest;
-      if (nodes.length != 1) {
-        print('Must specify exactly one node id after "node"');
-        exit(1);
-      }
-      var id = int.parse(nodes[0]);
-      var node = reader.deserializer.nodeForId(id);
-      for (var step in reader.propagationSteps) {
-        if (step is DownstreamPropagationStep &&
-            identical(node, step.targetNode)) {
-          print('Trace');
-          int i = 0;
-          while (step != null) {
-            var codeReference = step.codeReference;
-            var codeReferencePrefix =
-                codeReference == null ? '' : '$codeReference: ';
-            print('#${i++}\t$codeReferencePrefix$step');
-            step = step.principalCause;
-          }
-        }
-      }
-      break;
-    case 'trace_lengths':
-      for (var step in reader.propagationSteps) {
-        if (step is DownstreamPropagationStep) {
-          print('trace length ${_traceLength(step)} for node id '
-              '${reader.deserializer.idForNode(step.targetNode)}');
-        }
-      }
-      break;
-    case 'files':
-      for (var entry in reader.fileDecorations.keys) {
-        print(entry);
-      }
-      break;
-    case 'decorations':
-      var paths = command.rest;
-      if (paths.length != 1) {
-        print('Must specify exactly one path after "decorations"');
-        exit(1);
-      }
-      var path = paths[0];
-      var decorations = reader.fileDecorations[path];
-      if (decorations == null) {
-        print('Path not found: $path');
-        exit(1);
-      }
-      for (var decorationEntry in decorations.entries) {
-        for (var roleEntry in decorationEntry.value.entries) {
-          var span = Variables.spanForUniqueIdentifier(decorationEntry.key);
-          var nodeId = reader.idMapper.idForNode(roleEntry.value);
-          print('${span.offset}-${span.end}${roleEntry.key}: $nodeId');
-        }
-      }
-      break;
-    case 'node':
-      var nodes = command.rest;
-      if (nodes.length != 1) {
-        print('Must specify exactly one node id after "node"');
-        exit(1);
-      }
-      var id = int.parse(nodes[0]);
-      var node = reader.deserializer.nodeForId(id);
-      print('Node $id: $node');
-      print('Decorations:');
-      reader.findDecorationsByNode(node, (path, span, role) {
-        print('  $path:$span$role');
-      });
-      print('Upstream edges:');
-      for (var edge in node.upstreamEdges) {
-        var description =
-            (edge as NullabilityEdge).toString(idMapper: reader.idMapper);
-        print('  $description');
-      }
-      print('Downstream edges:');
-      for (var edge in node.downstreamEdges) {
-        var description =
-            (edge as NullabilityEdge).toString(idMapper: reader.idMapper);
-        print('  $description');
-      }
-      if (node is NullabilityNodeCompound) {
-        var componentsByName = node.componentsByName;
-        print('Components:');
-        for (var entry in componentsByName.entries) {
-          var description = entry.value.toString(idMapper: reader.idMapper);
-          print('  ${entry.key}: $description');
-        }
-      }
-      break;
-    default:
-      throw StateError('Unrecognized command: $command');
-  }
-}
-
-int _traceLength(PropagationStep step) {
-  int traceLength = 0;
-  while (step != null) {
-    traceLength++;
-    step = step.principalCause;
-  }
-  return traceLength;
-}
+void main(List<String> args) {}
 
 class Subcommand {
   final String name;
diff --git a/pkg/vm/lib/transformations/call_site_annotator.dart b/pkg/vm/lib/transformations/call_site_annotator.dart
index c843506..9ca67ac 100644
--- a/pkg/vm/lib/transformations/call_site_annotator.dart
+++ b/pkg/vm/lib/transformations/call_site_annotator.dart
@@ -50,14 +50,13 @@
     _staticTypeContext = null;
   }
 
-  void annotateWithType(TreeNode node, Expression receiver) {
-    _metadata.mapping[node] = new CallSiteAttributesMetadata(
-        receiverType: receiver.getStaticType(_staticTypeContext!));
+  void annotateWithReceiver(TreeNode node, Expression receiver) {
+    annotateWithReceiverType(node, receiver.getStaticType(_staticTypeContext!));
   }
 
-  void annotateWithFunctionType(TreeNode node, FunctionType type) {
+  void annotateWithReceiverType(TreeNode node, DartType receiverType) {
     _metadata.mapping[node] =
-        new CallSiteAttributesMetadata(receiverType: type);
+        new CallSiteAttributesMetadata(receiverType: receiverType);
   }
 
   @override
@@ -65,7 +64,7 @@
     super.visitPropertySet(node);
 
     if (hasGenericCovariantParameters(node.interfaceTarget)) {
-      annotateWithType(node, node.receiver);
+      annotateWithReceiver(node, node.receiver);
     }
   }
 
@@ -74,7 +73,7 @@
     super.visitInstanceSet(node);
 
     if (hasGenericCovariantParameters(node.interfaceTarget)) {
-      annotateWithType(node, node.receiver);
+      annotateWithReceiver(node, node.receiver);
     }
   }
 
@@ -86,7 +85,7 @@
     // or not it's a statically-checked call.
     if (node.name.text == 'call' ||
         hasGenericCovariantParameters(node.interfaceTarget)) {
-      annotateWithType(node, node.receiver);
+      annotateWithReceiver(node, node.receiver);
     }
   }
 
@@ -94,29 +93,31 @@
   visitInstanceInvocation(InstanceInvocation node) {
     super.visitInstanceInvocation(node);
 
+    final DartType receiverType =
+        node.receiver.getStaticType(_staticTypeContext!);
+    if (receiverType is FunctionType && node.name.text == 'call') {
+      throw 'Node ${node.runtimeType}: $node at ${node.location} has receiver'
+          ' static type $receiverType and selector \'call\'';
+    }
+
     // TODO(34162): We don't need to save the type here for calls, just whether
     // or not it's a statically-checked call.
     if (hasGenericCovariantParameters(node.interfaceTarget)) {
-      annotateWithType(node, node.receiver);
+      annotateWithReceiverType(node, receiverType);
     }
   }
 
   @override
-  visitLocalFunctionInvocation(LocalFunctionInvocation node) {
-    super.visitLocalFunctionInvocation(node);
-
-    // TODO(34162): We don't need to save the type here for calls, just whether
-    // or not it's a statically-checked call.
-    annotateWithFunctionType(node, node.functionType);
-  }
-
-  @override
   visitFunctionInvocation(FunctionInvocation node) {
     super.visitFunctionInvocation(node);
 
-    // TODO(34162): We don't need to save the type here for calls, just whether
-    // or not it's a statically-checked call.
-    annotateWithType(node, node.receiver);
+    final DartType receiverType =
+        node.receiver.getStaticType(_staticTypeContext!);
+    if (receiverType is FunctionType &&
+        node.kind == FunctionAccessKind.Function) {
+      throw 'Node ${node.runtimeType}: $node at ${node.location} has receiver'
+          ' static type $receiverType, but kind ${node.kind}';
+    }
   }
 
   @override
@@ -126,7 +127,7 @@
     // TODO(34162): We don't need to save the type here for calls, just whether
     // or not it's a statically-checked call.
     if (hasGenericCovariantParameters(node.interfaceTarget)) {
-      annotateWithType(node, node.left);
+      annotateWithReceiver(node, node.left);
     }
   }
 
diff --git a/pkg/vm/lib/transformations/ffi_native.dart b/pkg/vm/lib/transformations/ffi_native.dart
index 3d67aae..f3fdb03 100644
--- a/pkg/vm/lib/transformations/ffi_native.dart
+++ b/pkg/vm/lib/transformations/ffi_native.dart
@@ -100,14 +100,15 @@
     // patch files.
 
     // _ffi_resolver('dart:math', 'Math_sqrt')
-    final resolverInvocation = MethodInvocation(
+    final resolverInvocation = FunctionInvocation(
+        FunctionAccessKind.FunctionType,
         StaticGet(resolverField),
-        Name('call'),
         Arguments([
           ConstantExpression(
               StringConstant(currentLibrary!.importUri.toString())),
           ConstantExpression(functionName)
-        ]));
+        ]),
+        functionType: resolverField.type as FunctionType);
 
     // _fromAddress<NativeFunction<Double Function(Double)>>(...)
     final fromAddressInvocation = StaticInvocation(fromAddressInternal,
@@ -130,8 +131,11 @@
     currentLibrary!.addField(funcPtrField);
 
     // _@FfiNative_Math_sqrt(x)
-    final callFuncPtrInvocation = MethodInvocation(StaticGet(funcPtrField),
-        Name('call'), Arguments(params.map((p) => VariableGet(p)).toList()));
+    final callFuncPtrInvocation = FunctionInvocation(
+        FunctionAccessKind.FunctionType,
+        StaticGet(funcPtrField),
+        Arguments(params.map((p) => VariableGet(p)).toList()),
+        functionType: dartType as FunctionType);
 
     return ReturnStatement(callFuncPtrInvocation);
   }
diff --git a/pkg/vm/testcases/transformations/deferred_loading/main.dart.expect b/pkg/vm/testcases/transformations/deferred_loading/main.dart.expect
index 92b3481..0d07eae 100644
--- a/pkg/vm/testcases/transformations/deferred_loading/main.dart.expect
+++ b/pkg/vm/testcases/transformations/deferred_loading/main.dart.expect
@@ -67,7 +67,7 @@
       }
     :async_op_then = dart.async::_asyncThenWrapperHelper(:async_op);
     :async_op_error = dart.async::_asyncErrorWrapperHelper(:async_op);
-    [@vm.call-site-attributes.metadata=receiverType:dynamic Function()*] :async_op(){() →* dynamic};
+    :async_op(){() →* dynamic};
     :is_sync = true;
     return :async_future;
   }
@@ -98,7 +98,7 @@
       }
     :async_op_then = dart.async::_asyncThenWrapperHelper(:async_op);
     :async_op_error = dart.async::_asyncErrorWrapperHelper(:async_op);
-    [@vm.call-site-attributes.metadata=receiverType:dynamic Function()*] :async_op(){() →* dynamic};
+    :async_op(){() →* dynamic};
     :is_sync = true;
     return :async_future;
   }
@@ -135,7 +135,7 @@
       }
     :async_op_then = dart.async::_asyncThenWrapperHelper(:async_op);
     :async_op_error = dart.async::_asyncErrorWrapperHelper(:async_op);
-    [@vm.call-site-attributes.metadata=receiverType:dynamic Function()*] :async_op(){() →* dynamic};
+    :async_op(){() →* dynamic};
     :is_sync = true;
     return :async_future;
   }
@@ -166,7 +166,7 @@
       }
     :async_op_then = dart.async::_asyncThenWrapperHelper(:async_op);
     :async_op_error = dart.async::_asyncErrorWrapperHelper(:async_op);
-    [@vm.call-site-attributes.metadata=receiverType:dynamic Function()*] :async_op(){() →* dynamic};
+    :async_op(){() →* dynamic};
     :is_sync = true;
     return :async_future;
   }
@@ -207,7 +207,7 @@
       }
     :async_op_then = dart.async::_asyncThenWrapperHelper(:async_op);
     :async_op_error = dart.async::_asyncErrorWrapperHelper(:async_op);
-    [@vm.call-site-attributes.metadata=receiverType:dynamic Function()*] :async_op(){() →* dynamic};
+    :async_op(){() →* dynamic};
     :is_sync = true;
     return :async_future;
   }
@@ -243,7 +243,7 @@
       }
     :async_op_then = dart.async::_asyncThenWrapperHelper(:async_op);
     :async_op_error = dart.async::_asyncErrorWrapperHelper(:async_op);
-    [@vm.call-site-attributes.metadata=receiverType:dynamic Function()*] :async_op(){() →* dynamic};
+    :async_op(){() →* dynamic};
     :is_sync = true;
     return :async_future;
   }
@@ -280,7 +280,7 @@
       }
     :async_op_then = dart.async::_asyncThenWrapperHelper(:async_op);
     :async_op_error = dart.async::_asyncErrorWrapperHelper(:async_op);
-    [@vm.call-site-attributes.metadata=receiverType:dynamic Function()*] :async_op(){() →* dynamic};
+    :async_op(){() →* dynamic};
     :is_sync = true;
     return :async_future;
   }
@@ -313,7 +313,7 @@
       }
     :async_op_then = dart.async::_asyncThenWrapperHelper(:async_op);
     :async_op_error = dart.async::_asyncErrorWrapperHelper(:async_op);
-    [@vm.call-site-attributes.metadata=receiverType:dynamic Function()*] :async_op(){() →* dynamic};
+    :async_op(){() →* dynamic};
     :is_sync = true;
     return :async_future;
   }
@@ -346,7 +346,7 @@
       }
     :async_op_then = dart.async::_asyncThenWrapperHelper(:async_op);
     :async_op_error = dart.async::_asyncErrorWrapperHelper(:async_op);
-    [@vm.call-site-attributes.metadata=receiverType:dynamic Function()*] :async_op(){() →* dynamic};
+    :async_op(){() →* dynamic};
     :is_sync = true;
     return :async_future;
   }
@@ -382,7 +382,7 @@
       }
     :async_op_then = dart.async::_asyncThenWrapperHelper(:async_op);
     :async_op_error = dart.async::_asyncErrorWrapperHelper(:async_op);
-    [@vm.call-site-attributes.metadata=receiverType:dynamic Function()*] :async_op(){() →* dynamic};
+    :async_op(){() →* dynamic};
     :is_sync = true;
     return :async_future;
   }
@@ -418,7 +418,7 @@
       }
     :async_op_then = dart.async::_asyncThenWrapperHelper(:async_op);
     :async_op_error = dart.async::_asyncErrorWrapperHelper(:async_op);
-    [@vm.call-site-attributes.metadata=receiverType:dynamic Function()*] :async_op(){() →* dynamic};
+    :async_op(){() →* dynamic};
     :is_sync = true;
     return :async_future;
   }
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/async_await.dart.expect b/pkg/vm/testcases/transformations/type_flow/transformer/async_await.dart.expect
index 8922930..d1f3c4e 100644
--- a/pkg/vm/testcases/transformations/type_flow/transformer/async_await.dart.expect
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/async_await.dart.expect
@@ -31,7 +31,7 @@
       }
     :async_op_then = asy::_asyncThenWrapperHelper(:async_op);
     :async_op_error = asy::_asyncErrorWrapperHelper(:async_op);
-    [@vm.call-site-attributes.metadata=receiverType:dynamic Function()*] :async_op(){() →* dynamic};
+    :async_op(){() →* dynamic};
     :is_sync = true;
     return :async_future;
   }
@@ -66,7 +66,7 @@
     }
   :async_op_then = asy::_asyncThenWrapperHelper(:async_op);
   :async_op_error = asy::_asyncErrorWrapperHelper(:async_op);
-  [@vm.call-site-attributes.metadata=receiverType:dynamic Function()*] :async_op(){() →* dynamic};
+  :async_op(){() →* dynamic};
   :is_sync = true;
   return :async_future;
 }
@@ -97,7 +97,7 @@
     }
   :async_op_then = asy::_asyncThenWrapperHelper(:async_op);
   :async_op_error = asy::_asyncErrorWrapperHelper(:async_op);
-  [@vm.call-site-attributes.metadata=receiverType:dynamic Function()*] :async_op(){() →* dynamic};
+  :async_op(){() →* dynamic};
   :is_sync = true;
   return :async_future;
 }
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/ffi_struct_constructors.dart.expect b/pkg/vm/testcases/transformations/type_flow/transformer/ffi_struct_constructors.dart.expect
index e764dac..f657ded 100644
--- a/pkg/vm/testcases/transformations/type_flow/transformer/ffi_struct_constructors.dart.expect
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/ffi_struct_constructors.dart.expect
@@ -68,7 +68,7 @@
   final () →* self::Struct1* function1 = block {
     _in::_nativeEffect(new self::Struct1::#fromTypedDataBase([@vm.inferred-type.metadata=dart.typed_data::_Uint8List] typ::Uint8List::•(#C18)));
   } =>ffi::_asFunctionInternal<() →* self::Struct1*, () →* self::Struct1*>([@vm.direct-call.metadata=dart.ffi::DynamicLibrary.lookup??] [@vm.inferred-type.metadata=dart.ffi::Pointer? (skip check)] dylib.{ffi::DynamicLibrary::lookup}<ffi::NativeFunction<() →* self::Struct1*>*>("function1"){(core::String) → ffi::Pointer<ffi::NativeFunction<() →* self::Struct1*>*>}, false);
-  final self::Struct1* struct1 = [@vm.call-site-attributes.metadata=receiverType:#lib::Struct1* Function()*] function1(){() →* self::Struct1*};
+  final self::Struct1* struct1 = function1(){() →* self::Struct1*};
   core::print(struct1);
 }
 static method testAsFunctionReturn() → void {
@@ -76,7 +76,7 @@
   final () →* self::Struct2* function2 = block {
     _in::_nativeEffect(new self::Struct2::#fromTypedDataBase([@vm.inferred-type.metadata=dart.typed_data::_Uint8List] typ::Uint8List::•(#C18)));
   } =>ffi::_asFunctionInternal<() →* self::Struct2*, () →* self::Struct2*>(pointer, false);
-  final self::Struct2* struct2 = [@vm.call-site-attributes.metadata=receiverType:#lib::Struct2* Function()*] function2(){() →* self::Struct2*};
+  final self::Struct2* struct2 = function2(){() →* self::Struct2*};
   core::print(struct2);
 }
 [@vm.unboxing-info.metadata=(b)->i]static method useStruct3(self::Struct3* struct3) → core::int* {
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/future_or.dart.expect b/pkg/vm/testcases/transformations/type_flow/transformer/future_or.dart.expect
index 4a850d3..4c9cbc4 100644
--- a/pkg/vm/testcases/transformations/type_flow/transformer/future_or.dart.expect
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/future_or.dart.expect
@@ -57,7 +57,7 @@
   self::foo2_a4(a4);
 }
 static method getDynamic() → dynamic
-  return [@vm.call-site-attributes.metadata=receiverType:dart.core::Function*] self::unknown();
+  return self::unknown();
 static method main(core::List<core::String*>* args) → dynamic {
   self::foo1([@vm.inferred-type.metadata=dart.async::_Future<#lib::B*>] asy::Future::value<self::B*>(new self::B::•()), new self::B::•(), [@vm.inferred-type.metadata=dart.async::_Future<#lib::B*>] asy::Future::value<self::B*>(new self::B::•()), new self::B::•());
   self::foo2(self::getDynamic() as{TypeError,ForDynamic} asy::Future<self::A*>*, self::getDynamic() as{TypeError,ForDynamic} self::A*, self::getDynamic() as{TypeError,ForDynamic} FutureOr<self::A*>*, self::getDynamic() as{TypeError,ForDynamic} FutureOr<self::A*>*);
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/invalidation_field_initializer.dart.expect b/pkg/vm/testcases/transformations/type_flow/transformer/invalidation_field_initializer.dart.expect
index 09df5d9..9549477 100644
--- a/pkg/vm/testcases/transformations/type_flow/transformer/invalidation_field_initializer.dart.expect
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/invalidation_field_initializer.dart.expect
@@ -64,7 +64,7 @@
 [@vm.inferred-type.metadata=dart.core::Null? (value: null)]static field core::Function* unknown;
 static field core::Object* field1 = [@vm.inferred-type.metadata=!] self::getValue();
 static method getDynamic() → dynamic
-  return [@vm.call-site-attributes.metadata=receiverType:dart.core::Function*] self::unknown();
+  return self::unknown();
 static method getValue() → core::Object* {
   self::A* aa = self::getDynamic() as{TypeError,ForDynamic} self::A*;
   return [@vm.inferred-type.metadata=!] aa.{self::A::foo}(){() →* core::Object*};
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/invalidation_new_class1.dart.expect b/pkg/vm/testcases/transformations/type_flow/transformer/invalidation_new_class1.dart.expect
index 6f62466..750a33e 100644
--- a/pkg/vm/testcases/transformations/type_flow/transformer/invalidation_new_class1.dart.expect
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/invalidation_new_class1.dart.expect
@@ -33,7 +33,7 @@
 static method use2([@vm.inferred-type.metadata=#lib::Intermediate] self::Intermediate* i, [@vm.inferred-type.metadata=#lib::B?] self::A* aa) → dynamic
   return [@vm.direct-call.metadata=#lib::Intermediate.bar] [@vm.inferred-type.metadata=#lib::T1 (skip check)] i.{self::Intermediate::bar}(aa){(self::A*) →* dynamic};
 static method getDynamic() → dynamic
-  return [@vm.call-site-attributes.metadata=receiverType:dart.core::Function*] self::unknown();
+  return self::unknown();
 static method allocateB() → dynamic {
   new self::B::•();
 }
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/invalidation_new_class2.dart.expect b/pkg/vm/testcases/transformations/type_flow/transformer/invalidation_new_class2.dart.expect
index a9156b1..50f57c2 100644
--- a/pkg/vm/testcases/transformations/type_flow/transformer/invalidation_new_class2.dart.expect
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/invalidation_new_class2.dart.expect
@@ -59,7 +59,7 @@
 static method use3([@vm.inferred-type.metadata=#lib::Intermediate] self::Intermediate* i, self::A* aa) → dynamic
   return [@vm.direct-call.metadata=#lib::Intermediate.bar] [@vm.inferred-type.metadata=! (skip check)] i.{self::Intermediate::bar}(aa){(self::A*) →* dynamic};
 static method getDynamic() → dynamic
-  return [@vm.call-site-attributes.metadata=receiverType:dart.core::Function*] self::unknown();
+  return self::unknown();
 static method allocateB() → dynamic {
   new self::B::•();
 }
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/invalidation_new_dynamic_target.dart.expect b/pkg/vm/testcases/transformations/type_flow/transformer/invalidation_new_dynamic_target.dart.expect
index bc21869..13a7d78b 100644
--- a/pkg/vm/testcases/transformations/type_flow/transformer/invalidation_new_dynamic_target.dart.expect
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/invalidation_new_dynamic_target.dart.expect
@@ -49,7 +49,7 @@
 static method use_bazz(dynamic x) → dynamic
   return [@vm.inferred-type.metadata=#lib::T3] x{dynamic}.bazz();
 static method getDynamic() → dynamic
-  return [@vm.call-site-attributes.metadata=receiverType:dart.core::Function*] self::unknown();
+  return self::unknown();
 static method allocateA() → dynamic {
   new self::A::•();
 }
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/invalidation_set_field.dart.expect b/pkg/vm/testcases/transformations/type_flow/transformer/invalidation_set_field.dart.expect
index f07cc2b..85add1b 100644
--- a/pkg/vm/testcases/transformations/type_flow/transformer/invalidation_set_field.dart.expect
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/invalidation_set_field.dart.expect
@@ -51,7 +51,7 @@
 static method use2([@vm.inferred-type.metadata=#lib::DeepCaller2] self::DeepCaller2* x, [@vm.inferred-type.metadata=#lib::A?] self::A* aa) → dynamic
   return [@vm.direct-call.metadata=#lib::DeepCaller2.barL1] [@vm.inferred-type.metadata=! (skip check)] x.{self::DeepCaller2::barL1}(aa){(self::A*) →* dynamic};
 static method getDynamic() → dynamic
-  return [@vm.call-site-attributes.metadata=receiverType:dart.core::Function*] self::unknown();
+  return self::unknown();
 static method setField2([@vm.inferred-type.metadata=#lib::A] self::A* aa, [@vm.inferred-type.metadata=#lib::T2] dynamic value) → void {
   [@vm.direct-call.metadata=#lib::A.field2] [@vm.inferred-type.metadata=!? (skip check)] aa.{self::A::field2} = value;
 }
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/no_such_method.dart.expect b/pkg/vm/testcases/transformations/type_flow/transformer/no_such_method.dart.expect
index 42078a7..719f96e 100644
--- a/pkg/vm/testcases/transformations/type_flow/transformer/no_such_method.dart.expect
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/no_such_method.dart.expect
@@ -114,7 +114,7 @@
 [@vm.inferred-type.metadata=#lib::D?]static field self::A* dd = new self::D::•();
 [@vm.inferred-type.metadata=dart.core::Null? (value: null)]static field core::Function* unknown;
 static method getDynamic() → dynamic
-  return [@vm.call-site-attributes.metadata=receiverType:dart.core::Function*] self::unknown();
+  return self::unknown();
 static method main(core::List<core::String*>* args) → dynamic {
   core::print([@vm.direct-call.metadata=#lib::B.foo??] [@vm.inferred-type.metadata=#lib::T1 (skip check)] [@vm.inferred-type.metadata=#lib::B?] self::bb.{self::A::foo}(){() →* dynamic});
   core::print([@vm.direct-call.metadata=#lib::B.bar??] [@vm.inferred-type.metadata=#lib::T1] [@vm.inferred-type.metadata=#lib::B?] self::bb.{self::A::bar}{dynamic});
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/param_types_before_strong_mode_checks.dart.expect b/pkg/vm/testcases/transformations/type_flow/transformer/param_types_before_strong_mode_checks.dart.expect
index a21a679..90996865 100644
--- a/pkg/vm/testcases/transformations/type_flow/transformer/param_types_before_strong_mode_checks.dart.expect
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/param_types_before_strong_mode_checks.dart.expect
@@ -49,9 +49,9 @@
   [@vm.direct-call.metadata=#lib::T2.foo??] [@vm.inferred-type.metadata=!? (skip check)] t0.{self::T0::foo}(){() →* void};
 }
 static method getDynamic() → dynamic
-  return [@vm.call-site-attributes.metadata=receiverType:dart.core::Function*] self::unknown();
+  return self::unknown();
 static method use(dynamic x) → dynamic
-  return [@vm.call-site-attributes.metadata=receiverType:dart.core::Function*] self::unknown(x);
+  return self::unknown(x);
 static method main(core::List<core::String*>* args) → dynamic {
   self::func1(self::getDynamic() as{TypeError,ForDynamic} self::T0*);
   self::use(#C1);
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/pragmas.dart.expect b/pkg/vm/testcases/transformations/type_flow/transformer/pragmas.dart.expect
index 84c335d..b832065 100644
--- a/pkg/vm/testcases/transformations/type_flow/transformer/pragmas.dart.expect
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/pragmas.dart.expect
@@ -20,7 +20,7 @@
   method bar() → void {
     @#C17
     function bazz() → void {}
-    [@vm.call-site-attributes.metadata=receiverType:void Function()*] bazz(){() →* void};
+    bazz(){() →* void};
   }
 }
 static method main() → dynamic {
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/regress_flutter16182.dart.expect b/pkg/vm/testcases/transformations/type_flow/transformer/regress_flutter16182.dart.expect
index c812ae8..7937844 100644
--- a/pkg/vm/testcases/transformations/type_flow/transformer/regress_flutter16182.dart.expect
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/regress_flutter16182.dart.expect
@@ -130,7 +130,7 @@
   exp::Expect::isTrue([@vm.inferred-type.metadata=dart.core::bool?] self::ok);
 }
 static method getDynamic3() → dynamic
-  return [@vm.call-site-attributes.metadata=receiverType:dart.core::Function*] self::unknown3();
+  return self::unknown3();
 static method test3() → void {
   self::getDynamic3(){dynamic}.aa3(1, 2, 3, 4, 5, 6, new self::T3::•());
   self::ok = false;
@@ -138,7 +138,7 @@
   exp::Expect::isTrue([@vm.inferred-type.metadata=dart.core::bool?] self::ok);
 }
 static method getDynamic4() → dynamic
-  return [@vm.call-site-attributes.metadata=receiverType:dart.core::Function*] self::unknown4();
+  return self::unknown4();
 static method test4() → void {
   self::getDynamic4(){dynamic}.aa4(1, 2, 3, 4, 5, 6, 7, new self::T4::•());
   self::ok = false;
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/tear_off_dynamic_method.dart.expect b/pkg/vm/testcases/transformations/type_flow/transformer/tear_off_dynamic_method.dart.expect
index a3f1c3a..3483f2c 100644
--- a/pkg/vm/testcases/transformations/type_flow/transformer/tear_off_dynamic_method.dart.expect
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/tear_off_dynamic_method.dart.expect
@@ -25,5 +25,5 @@
   return new self::B::•();
 static method main(core::List<core::String*>* args) → dynamic {
   core::Function* closure = () → self::B* => new self::B::•();
-  new self::TearOffDynamicMethod::•([@vm.call-site-attributes.metadata=receiverType:dart.core::Function*] closure());
+  new self::TearOffDynamicMethod::•(closure());
 }
diff --git a/runtime/bin/ffi_test/ffi_test_functions_vmspecific.cc b/runtime/bin/ffi_test/ffi_test_functions_vmspecific.cc
index 87e9d66..8564aa6 100644
--- a/runtime/bin/ffi_test/ffi_test_functions_vmspecific.cc
+++ b/runtime/bin/ffi_test/ffi_test_functions_vmspecific.cc
@@ -314,6 +314,15 @@
 
 #endif  // defined(TARGET_OS_LINUX)
 
+// Restore default SIGPIPE handler, which is only needed on mac
+// since that is the only platform we explicitly ignore it.
+// See Platform::Initialize() in platform_macos.cc.
+DART_EXPORT void RestoreSIGPIPEHandler() {
+#if defined(HOST_OS_MACOS)
+  signal(SIGPIPE, SIG_DFL);
+#endif
+}
+
 DART_EXPORT void IGH_MsanUnpoison(void* start, intptr_t length) {
   MSAN_UNPOISON(start, length);
 }
@@ -418,7 +427,7 @@
 
 #define FATAL(error) Fatal(__FILE__, __LINE__, error)
 
-void SleepOnAnyOS(intptr_t seconds) {
+DART_EXPORT void SleepOnAnyOS(intptr_t seconds) {
 #if defined(HOST_OS_WINDOWS)
   Sleep(1000 * seconds);
 #else
diff --git a/runtime/bin/socket_macos.cc b/runtime/bin/socket_macos.cc
index 8c6be8b..f9e00c2 100644
--- a/runtime/bin/socket_macos.cc
+++ b/runtime/bin/socket_macos.cc
@@ -44,6 +44,11 @@
     FDUtils::SaveErrorAndClose(fd);
     return -1;
   }
+  // Don't raise SIGPIPE when attempting to write to a connection which has
+  // already closed.
+  int optval = 1;
+  VOID_NO_RETRY_EXPECTED(
+      setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &optval, sizeof(optval)));
   return fd;
 }
 
diff --git a/runtime/vm/class_finalizer.cc b/runtime/vm/class_finalizer.cc
index eff5ead..a565e55 100644
--- a/runtime/vm/class_finalizer.cc
+++ b/runtime/vm/class_finalizer.cc
@@ -732,15 +732,19 @@
     return type.ptr();
   }
 
-  if (type.IsTypeRef()) {
-    // The referenced type will be finalized later by the code that set the
-    // is_being_finalized mark bit.
-    return type.ptr();
-  }
+  Thread* thread = Thread::Current();
+  Zone* zone = thread->zone();
 
-  if (type.IsTypeParameter() && type.IsBeingFinalized()) {
-    // The base and index have already been adjusted, but the bound referring
-    // back to the type parameter is still being finalized.
+  if (type.IsTypeRef()) {
+    if (type.IsBeingFinalized()) {
+      // The referenced type will be finalized later by the code that set the
+      // is_being_finalized mark bit.
+      return type.ptr();
+    }
+    AbstractType& ref_type =
+        AbstractType::Handle(zone, TypeRef::Cast(type).type());
+    ref_type = FinalizeType(ref_type, finalization, pending_types);
+    TypeRef::Cast(type).set_type(ref_type);
     return type.ptr();
   }
 
@@ -751,9 +755,6 @@
   // Mark the type as being finalized in order to detect self reference.
   type.SetIsBeingFinalized();
 
-  Thread* thread = Thread::Current();
-  Zone* zone = thread->zone();
-
   if (FLAG_trace_type_finalization) {
     THR_Print("Finalizing type '%s'\n",
               String::Handle(zone, type.Name()).ToCString());
@@ -780,21 +781,13 @@
       type_parameter.set_parameterized_class_id(kClassCid);
     }
 
+    type_parameter.SetIsFinalized();
     AbstractType& upper_bound = AbstractType::Handle(zone);
     upper_bound = type_parameter.bound();
-    if (upper_bound.IsBeingFinalized()) {
-      if (upper_bound.IsTypeRef()) {
-        // Nothing to do.
-      } else {
-        upper_bound = TypeRef::New(upper_bound);
-        type_parameter.set_bound(upper_bound);
-        upper_bound = FinalizeType(upper_bound, kFinalize);
-      }
-    } else {
+    if (!upper_bound.IsBeingFinalized()) {
       upper_bound = FinalizeType(upper_bound, kFinalize);
       type_parameter.set_bound(upper_bound);
     }
-    type_parameter.SetIsFinalized();
 
     if (FLAG_trace_type_finalization) {
       THR_Print("Done finalizing type parameter at index %" Pd "\n",
diff --git a/runtime/vm/compiler/asm_intrinsifier_arm.cc b/runtime/vm/compiler/asm_intrinsifier_arm.cc
index f41c51d..134a71d 100644
--- a/runtime/vm/compiler/asm_intrinsifier_arm.cc
+++ b/runtime/vm/compiler/asm_intrinsifier_arm.cc
@@ -1272,15 +1272,19 @@
 
 // Compares cid1 and cid2 to see if they're syntactically equivalent. If this
 // can be determined by this fast path, it jumps to either equal or not_equal,
-// otherwise it jumps to normal_ir_body. May clobber cid1, cid2, and scratch.
+// if equal but belonging to a generic class, it falls through with the scratch
+// register containing host_type_arguments_field_offset_in_words,
+// otherwise it jumps to normal_ir_body. May clobber scratch.
 static void EquivalentClassIds(Assembler* assembler,
                                Label* normal_ir_body,
                                Label* equal,
                                Label* not_equal,
                                Register cid1,
                                Register cid2,
-                               Register scratch) {
-  Label different_cids, not_integer, not_integer_or_string;
+                               Register scratch,
+                               bool testing_instance_cids) {
+  Label different_cids, equal_cids_but_generic, not_integer,
+      not_integer_or_string;
 
   // Check if left hand side is a closure. Closures are handled in the runtime.
   __ CompareImmediate(cid1, kClosureCid);
@@ -1296,10 +1300,13 @@
   // Check if there are no type arguments. In this case we can return true.
   // Otherwise fall through into the runtime to handle comparison.
   __ LoadClassById(scratch, cid1);
-  __ ldrh(scratch,
-          FieldAddress(scratch, target::Class::num_type_arguments_offset()));
-  __ CompareImmediate(scratch, 0);
-  __ b(normal_ir_body, NE);
+  __ ldr(
+      scratch,
+      FieldAddress(
+          scratch,
+          target::Class::host_type_arguments_field_offset_in_words_offset()));
+  __ CompareImmediate(scratch, target::Class::kNoTypeArguments);
+  __ b(&equal_cids_but_generic, NE);
   __ b(equal);
 
   // Class ids are different. Check if we are comparing two string types (with
@@ -1318,35 +1325,50 @@
 
   __ Bind(&not_integer);
   // Check if both are String types.
-  JumpIfNotString(assembler, cid1, scratch, &not_integer_or_string);
+  JumpIfNotString(assembler, cid1, scratch,
+                  testing_instance_cids ? &not_integer_or_string : not_equal);
 
   // First type is String. Check if the second is a string too.
   JumpIfString(assembler, cid2, scratch, equal);
   // String types are only equivalent to other String types.
   __ b(not_equal);
 
-  __ Bind(&not_integer_or_string);
-  // Check if the first type is a Type. If it is not then types are not
-  // equivalent because they have different class ids and they are not String
-  // or integer or Type.
-  JumpIfNotType(assembler, cid1, scratch, not_equal);
+  if (testing_instance_cids) {
+    __ Bind(&not_integer_or_string);
+    // Check if the first type is a Type. If it is not then types are not
+    // equivalent because they have different class ids and they are not String
+    // or integer or Type.
+    JumpIfNotType(assembler, cid1, scratch, not_equal);
 
-  // First type is a Type. Check if the second is a Type too.
-  JumpIfType(assembler, cid2, scratch, equal);
-  // Type types are only equivalent to other Type types.
-  __ b(not_equal);
+    // First type is a Type. Check if the second is a Type too.
+    JumpIfType(assembler, cid2, scratch, equal);
+    // Type types are only equivalent to other Type types.
+    __ b(not_equal);
+  }
+
+  // The caller must compare the type arguments.
+  __ Bind(&equal_cids_but_generic);
 }
 
 void AsmIntrinsifier::ObjectHaveSameRuntimeType(Assembler* assembler,
                                                 Label* normal_ir_body) {
-  __ ldr(R0, Address(SP, 0 * target::kWordSize));
-  __ LoadClassIdMayBeSmi(R1, R0);
-
-  __ ldr(R0, Address(SP, 1 * target::kWordSize));
-  __ LoadClassIdMayBeSmi(R2, R0);
+  __ ldm(IA, SP, (1 << R1 | 1 << R2));
+  __ LoadClassIdMayBeSmi(R1, R1);
+  __ LoadClassIdMayBeSmi(R2, R2);
 
   Label equal, not_equal;
-  EquivalentClassIds(assembler, normal_ir_body, &equal, &not_equal, R1, R2, R0);
+  EquivalentClassIds(assembler, normal_ir_body, &equal, &not_equal, R1, R2, R0,
+                     /* testing_instance_cids = */ true);
+
+  // Compare type arguments, host_type_arguments_field_offset_in_words in R0.
+  __ ldm(IA, SP, (1 << R1 | 1 << R2));
+  __ AddImmediate(R1, -kHeapObjectTag);
+  __ ldr(R1, Address(R1, R0, LSL, target::kWordSizeLog2));
+  __ AddImmediate(R2, -kHeapObjectTag);
+  __ ldr(R2, Address(R2, R0, LSL, target::kWordSizeLog2));
+  __ cmp(R1, Operand(R2));
+  __ b(&not_equal, NE);
+  // Fall through to equal case if type arguments are equal.
 
   __ Bind(&equal);
   __ LoadObject(R0, CastHandle<Object>(TrueObject()));
@@ -1396,8 +1418,16 @@
   __ SmiUntag(R3);
   __ ldr(R4, FieldAddress(R2, target::Type::type_class_id_offset()));
   __ SmiUntag(R4);
+  // We are not testing instance cids, but type class cids of Type instances.
   EquivalentClassIds(assembler, normal_ir_body, &equiv_cids, &not_equal, R3, R4,
-                     R0);
+                     R0, /* testing_instance_cids = */ false);
+
+  // Compare type arguments in Type instances.
+  __ ldr(R3, FieldAddress(R1, target::Type::arguments_offset()));
+  __ ldr(R4, FieldAddress(R2, target::Type::arguments_offset()));
+  __ cmp(R3, Operand(R4));
+  __ b(normal_ir_body, NE);
+  // Fall through to check nullability if type arguments are equal.
 
   // Check nullability.
   __ Bind(&equiv_cids);
diff --git a/runtime/vm/compiler/asm_intrinsifier_arm64.cc b/runtime/vm/compiler/asm_intrinsifier_arm64.cc
index 5749365..04718ce 100644
--- a/runtime/vm/compiler/asm_intrinsifier_arm64.cc
+++ b/runtime/vm/compiler/asm_intrinsifier_arm64.cc
@@ -1418,15 +1418,19 @@
 
 // Compares cid1 and cid2 to see if they're syntactically equivalent. If this
 // can be determined by this fast path, it jumps to either equal or not_equal,
-// otherwise it jumps to normal_ir_body. May clobber cid1, cid2, and scratch.
+// if equal but belonging to a generic class, it falls through with the scratch
+// register containing host_type_arguments_field_offset_in_words,
+// otherwise it jumps to normal_ir_body. May clobber scratch.
 static void EquivalentClassIds(Assembler* assembler,
                                Label* normal_ir_body,
                                Label* equal,
                                Label* not_equal,
                                Register cid1,
                                Register cid2,
-                               Register scratch) {
-  Label different_cids, not_integer, not_integer_or_string;
+                               Register scratch,
+                               bool testing_instance_cids) {
+  Label different_cids, equal_cids_but_generic, not_integer,
+      not_integer_or_string;
 
   // Check if left hand side is a closure. Closures are handled in the runtime.
   __ CompareImmediate(cid1, kClosureCid);
@@ -1443,10 +1447,12 @@
   // Otherwise fall through into the runtime to handle comparison.
   __ LoadClassById(scratch, cid1);
   __ ldr(scratch,
-         FieldAddress(scratch, target::Class::num_type_arguments_offset(),
-                      kTwoBytes),
-         kTwoBytes);
-  __ cbnz(normal_ir_body, scratch);
+         FieldAddress(
+             scratch,
+             target::Class::host_type_arguments_field_offset_in_words_offset()),
+         kFourBytes);
+  __ CompareImmediate(scratch, target::Class::kNoTypeArguments);
+  __ b(&equal_cids_but_generic, NE);
   __ b(equal);
 
   // Class ids are different. Check if we are comparing two string types (with
@@ -1465,35 +1471,50 @@
 
   __ Bind(&not_integer);
   // Check if both are String types.
-  JumpIfNotString(assembler, cid1, scratch, &not_integer_or_string);
+  JumpIfNotString(assembler, cid1, scratch,
+                  testing_instance_cids ? &not_integer_or_string : not_equal);
 
   // First type is String. Check if the second is a string too.
   JumpIfString(assembler, cid2, scratch, equal);
   // String types are only equivalent to other String types.
   __ b(not_equal);
 
-  __ Bind(&not_integer_or_string);
-  // Check if the first type is a Type. If it is not then types are not
-  // equivalent because they have different class ids and they are not String
-  // or integer or Type.
-  JumpIfNotType(assembler, cid1, scratch, not_equal);
+  if (testing_instance_cids) {
+    __ Bind(&not_integer_or_string);
+    // Check if the first type is a Type. If it is not then types are not
+    // equivalent because they have different class ids and they are not String
+    // or integer or Type.
+    JumpIfNotType(assembler, cid1, scratch, not_equal);
 
-  // First type is a Type. Check if the second is a Type too.
-  JumpIfType(assembler, cid2, scratch, equal);
-  // Type types are only equivalent to other Type types.
-  __ b(not_equal);
+    // First type is a Type. Check if the second is a Type too.
+    JumpIfType(assembler, cid2, scratch, equal);
+    // Type types are only equivalent to other Type types.
+    __ b(not_equal);
+  }
+
+  // The caller must compare the type arguments.
+  __ Bind(&equal_cids_but_generic);
 }
 
 void AsmIntrinsifier::ObjectHaveSameRuntimeType(Assembler* assembler,
                                                 Label* normal_ir_body) {
-  __ ldr(R0, Address(SP, 0 * target::kWordSize));
+  __ ldp(R0, R1, Address(SP, 0 * target::kWordSize, Address::PairOffset));
+  __ LoadClassIdMayBeSmi(R2, R1);
   __ LoadClassIdMayBeSmi(R1, R0);
 
-  __ ldr(R0, Address(SP, 1 * target::kWordSize));
-  __ LoadClassIdMayBeSmi(R2, R0);
-
   Label equal, not_equal;
-  EquivalentClassIds(assembler, normal_ir_body, &equal, &not_equal, R1, R2, R0);
+  EquivalentClassIds(assembler, normal_ir_body, &equal, &not_equal, R1, R2, R0,
+                     /* testing_instance_cids = */ true);
+
+  // Compare type arguments, host_type_arguments_field_offset_in_words in R0.
+  __ ldp(R1, R2, Address(SP, 0 * target::kWordSize, Address::PairOffset));
+  __ AddImmediate(R1, -kHeapObjectTag);
+  __ ldr(R1, Address(R1, R0, UXTX, Address::Scaled));
+  __ AddImmediate(R2, -kHeapObjectTag);
+  __ ldr(R2, Address(R2, R0, UXTX, Address::Scaled));
+  __ CompareObjectRegisters(R1, R2);
+  __ b(&not_equal, NE);
+  // Fall through to equal case if type arguments are equal.
 
   __ Bind(&equal);
   __ LoadObject(R0, CastHandle<Object>(TrueObject()));
@@ -1549,8 +1570,16 @@
   __ LoadCompressedSmi(R4,
                        FieldAddress(R2, target::Type::type_class_id_offset()));
   __ SmiUntag(R4);
+  // We are not testing instance cids, but type class cids of Type instances.
   EquivalentClassIds(assembler, normal_ir_body, &equiv_cids, &not_equal, R3, R4,
-                     R0);
+                     R0, /* testing_instance_cids = */ false);
+
+  // Compare type arguments in Type instances.
+  __ LoadCompressed(R3, FieldAddress(R1, target::Type::arguments_offset()));
+  __ LoadCompressed(R4, FieldAddress(R2, target::Type::arguments_offset()));
+  __ CompareObjectRegisters(R3, R4);
+  __ b(normal_ir_body, NE);
+  // Fall through to check nullability if type arguments are equal.
 
   // Check nullability.
   __ Bind(&equiv_cids);
diff --git a/runtime/vm/compiler/asm_intrinsifier_ia32.cc b/runtime/vm/compiler/asm_intrinsifier_ia32.cc
index 5909eab..f1cf3ac 100644
--- a/runtime/vm/compiler/asm_intrinsifier_ia32.cc
+++ b/runtime/vm/compiler/asm_intrinsifier_ia32.cc
@@ -1358,15 +1358,19 @@
 
 // Compares cid1 and cid2 to see if they're syntactically equivalent. If this
 // can be determined by this fast path, it jumps to either equal or not_equal,
-// otherwise it jumps to normal_ir_body. May clobber cid1, cid2, and scratch.
+// if equal but belonging to a generic class, it falls through with the scratch
+// register containing host_type_arguments_field_offset_in_words,
+// otherwise it jumps to normal_ir_body. May clobber scratch.
 static void EquivalentClassIds(Assembler* assembler,
                                Label* normal_ir_body,
                                Label* equal,
                                Label* not_equal,
                                Register cid1,
                                Register cid2,
-                               Register scratch) {
-  Label different_cids, not_integer, not_integer_or_string;
+                               Register scratch,
+                               bool testing_instance_cids) {
+  Label different_cids, equal_cids_but_generic, not_integer,
+      not_integer_or_string;
 
   // Check if left hand side is a closure. Closures are handled in the runtime.
   __ cmpl(cid1, Immediate(kClosureCid));
@@ -1382,10 +1386,13 @@
   // Check if there are no type arguments. In this case we can return true.
   // Otherwise fall through into the runtime to handle comparison.
   __ LoadClassById(scratch, cid1);
-  __ movzxw(scratch,
-            FieldAddress(scratch, target::Class::num_type_arguments_offset()));
-  __ cmpl(scratch, Immediate(0));
-  __ j(NOT_EQUAL, normal_ir_body);
+  __ movl(
+      scratch,
+      FieldAddress(
+          scratch,
+          target::Class::host_type_arguments_field_offset_in_words_offset()));
+  __ cmpl(scratch, Immediate(target::Class::kNoTypeArguments));
+  __ j(NOT_EQUAL, &equal_cids_but_generic, Assembler::kNearJump);
   __ jmp(equal);
 
   // Class ids are different. Check if we are comparing two string types (with
@@ -1405,23 +1412,29 @@
 
   __ Bind(&not_integer);
   // Check if both are String types.
-  JumpIfNotString(assembler, cid1, &not_integer_or_string);
+  JumpIfNotString(assembler, cid1,
+                  testing_instance_cids ? &not_integer_or_string : not_equal);
 
   // First type is a String. Check if the second is a String too.
   JumpIfString(assembler, cid2, equal);
   // String types are only equivalent to other String types.
   __ jmp(not_equal);
 
-  __ Bind(&not_integer_or_string);
-  // Check if the first type is a Type. If it is not then types are not
-  // equivalent because they have different class ids and they are not String
-  // or integer or Type.
-  JumpIfNotType(assembler, cid1, not_equal);
+  if (testing_instance_cids) {
+    __ Bind(&not_integer_or_string);
+    // Check if the first type is a Type. If it is not then types are not
+    // equivalent because they have different class ids and they are not String
+    // or integer or Type.
+    JumpIfNotType(assembler, cid1, not_equal);
 
-  // First type is a Type. Check if the second is a Type too.
-  JumpIfType(assembler, cid2, equal);
-  // Type types are only equivalent to other Type types.
-  __ jmp(not_equal);
+    // First type is a Type. Check if the second is a Type too.
+    JumpIfType(assembler, cid2, equal);
+    // Type types are only equivalent to other Type types.
+    __ jmp(not_equal);
+  }
+
+  // The caller must compare the type arguments.
+  __ Bind(&equal_cids_but_generic);
 }
 
 void AsmIntrinsifier::ObjectHaveSameRuntimeType(Assembler* assembler,
@@ -1434,7 +1447,16 @@
 
   Label equal, not_equal;
   EquivalentClassIds(assembler, normal_ir_body, &equal, &not_equal, EDI, EBX,
-                     EAX);
+                     EAX, /* testing_instance_cids = */ true);
+
+  // Compare type arguments, host_type_arguments_field_offset_in_words in EAX.
+  __ movl(EDI, Address(ESP, +1 * target::kWordSize));
+  __ movl(EBX, Address(ESP, +2 * target::kWordSize));
+  __ movl(EDI, FieldAddress(EDI, EAX, TIMES_4, 0));
+  __ movl(EBX, FieldAddress(EBX, EAX, TIMES_4, 0));
+  __ cmpl(EDI, EBX);
+  __ j(NOT_EQUAL, &not_equal, Assembler::kNearJump);
+  // Fall through to equal case if type arguments are equal.
 
   __ Bind(&equal);
   __ LoadObject(EAX, CastHandle<Object>(TrueObject()));
@@ -1489,8 +1511,16 @@
   __ SmiUntag(ECX);
   __ movl(EDX, FieldAddress(EBX, target::Type::type_class_id_offset()));
   __ SmiUntag(EDX);
+  // We are not testing instance cids, but type class cids of Type instances.
   EquivalentClassIds(assembler, normal_ir_body, &equiv_cids, &not_equal, ECX,
-                     EDX, EAX);
+                     EDX, EAX, /* testing_instance_cids = */ false);
+
+  // Compare type arguments in Type instances.
+  __ movl(ECX, FieldAddress(EDI, target::Type::arguments_offset()));
+  __ movl(EDX, FieldAddress(EBX, target::Type::arguments_offset()));
+  __ cmpl(ECX, EDX);
+  __ j(NOT_EQUAL, normal_ir_body, Assembler::kNearJump);
+  // Fall through to check nullability if type arguments are equal.
 
   // Check nullability.
   __ Bind(&equiv_cids);
diff --git a/runtime/vm/compiler/asm_intrinsifier_x64.cc b/runtime/vm/compiler/asm_intrinsifier_x64.cc
index f691931..d13388a 100644
--- a/runtime/vm/compiler/asm_intrinsifier_x64.cc
+++ b/runtime/vm/compiler/asm_intrinsifier_x64.cc
@@ -1262,15 +1262,19 @@
 
 // Compares cid1 and cid2 to see if they're syntactically equivalent. If this
 // can be determined by this fast path, it jumps to either equal or not_equal,
-// otherwise it jumps to normal_ir_body. May clobber cid1, cid2, and scratch.
+// if equal but belonging to a generic class, it falls through with the scratch
+// register containing host_type_arguments_field_offset_in_words,
+// otherwise it jumps to normal_ir_body. May clobber scratch.
 static void EquivalentClassIds(Assembler* assembler,
                                Label* normal_ir_body,
                                Label* equal,
                                Label* not_equal,
                                Register cid1,
                                Register cid2,
-                               Register scratch) {
-  Label different_cids, not_integer, not_integer_or_string;
+                               Register scratch,
+                               bool testing_instance_cids) {
+  Label different_cids, equal_cids_but_generic, not_integer,
+      not_integer_or_string;
 
   // Check if left hand side is a closure. Closures are handled in the runtime.
   __ cmpq(cid1, Immediate(kClosureCid));
@@ -1286,10 +1290,13 @@
   // Check if there are no type arguments. In this case we can return true.
   // Otherwise fall through into the runtime to handle comparison.
   __ LoadClassById(scratch, cid1);
-  __ movzxw(scratch,
-            FieldAddress(scratch, target::Class::num_type_arguments_offset()));
-  __ cmpq(scratch, Immediate(0));
-  __ j(NOT_EQUAL, normal_ir_body);
+  __ movl(
+      scratch,
+      FieldAddress(
+          scratch,
+          target::Class::host_type_arguments_field_offset_in_words_offset()));
+  __ cmpl(scratch, Immediate(target::Class::kNoTypeArguments));
+  __ j(NOT_EQUAL, &equal_cids_but_generic, Assembler::kNearJump);
   __ jmp(equal);
 
   // Class ids are different. Check if we are comparing two string types (with
@@ -1309,23 +1316,29 @@
 
   __ Bind(&not_integer);
   // Check if both are String types.
-  JumpIfNotString(assembler, cid1, &not_integer_or_string);
+  JumpIfNotString(assembler, cid1,
+                  testing_instance_cids ? &not_integer_or_string : not_equal);
 
   // First type is a String. Check if the second is a String too.
   JumpIfString(assembler, cid2, equal);
   // String types are only equivalent to other String types.
   __ jmp(not_equal);
 
-  __ Bind(&not_integer_or_string);
-  // Check if the first type is a Type. If it is not then types are not
-  // equivalent because they have different class ids and they are not String
-  // or integer or Type.
-  JumpIfNotType(assembler, cid1, not_equal);
+  if (testing_instance_cids) {
+    __ Bind(&not_integer_or_string);
+    // Check if the first type is a Type. If it is not then types are not
+    // equivalent because they have different class ids and they are not String
+    // or integer or Type.
+    JumpIfNotType(assembler, cid1, not_equal);
 
-  // First type is a Type. Check if the second is a Type too.
-  JumpIfType(assembler, cid2, equal);
-  // Type types are only equivalent to other Type types.
-  __ jmp(not_equal);
+    // First type is a Type. Check if the second is a Type too.
+    JumpIfType(assembler, cid2, equal);
+    // Type types are only equivalent to other Type types.
+    __ jmp(not_equal);
+  }
+
+  // The caller must compare the type arguments.
+  __ Bind(&equal_cids_but_generic);
 }
 
 void AsmIntrinsifier::ObjectHaveSameRuntimeType(Assembler* assembler,
@@ -1338,7 +1351,16 @@
 
   Label equal, not_equal;
   EquivalentClassIds(assembler, normal_ir_body, &equal, &not_equal, RCX, RDX,
-                     RAX);
+                     RAX, /* testing_instance_cids = */ true);
+
+  // Compare type arguments, host_type_arguments_field_offset_in_words in RAX.
+  __ movq(RCX, Address(RSP, +1 * target::kWordSize));
+  __ movq(RDX, Address(RSP, +2 * target::kWordSize));
+  __ movq(RCX, FieldAddress(RCX, RAX, TIMES_8, 0));
+  __ movq(RDX, FieldAddress(RDX, RAX, TIMES_8, 0));
+  __ cmpq(RCX, RDX);
+  __ j(NOT_EQUAL, &not_equal, Assembler::kNearJump);
+  // Fall through to equal case if type arguments are equal.
 
   __ Bind(&equal);
   __ LoadObject(RAX, CastHandle<Object>(TrueObject()));
@@ -1399,8 +1421,16 @@
   __ LoadCompressedSmi(RSI,
                        FieldAddress(RDX, target::Type::type_class_id_offset()));
   __ SmiUntag(RSI);
+  // We are not testing instance cids, but type class cids of Type instances.
   EquivalentClassIds(assembler, normal_ir_body, &equiv_cids, &not_equal, RDI,
-                     RSI, RAX);
+                     RSI, RAX, /* testing_instance_cids = */ false);
+
+  // Compare type arguments in Type instances.
+  __ LoadCompressed(RDI, FieldAddress(RCX, target::Type::arguments_offset()));
+  __ LoadCompressed(RSI, FieldAddress(RDX, target::Type::arguments_offset()));
+  __ cmpq(RDI, RSI);
+  __ j(NOT_EQUAL, normal_ir_body, Assembler::kNearJump);
+  // Fall through to check nullability if type arguments are equal.
 
   // Check nullability.
   __ Bind(&equiv_cids);
diff --git a/runtime/vm/compiler/backend/type_propagator.cc b/runtime/vm/compiler/backend/type_propagator.cc
index c396f37..c28b9c7 100644
--- a/runtime/vm/compiler/backend/type_propagator.cc
+++ b/runtime/vm/compiler/backend/type_propagator.cc
@@ -917,7 +917,8 @@
     // Comparable<int>).
     if (type.IsFutureOrType() ||
         type.type_class() == CompilerState::Current().ComparableClass().ptr()) {
-      const auto& args = TypeArguments::Handle(Type::Cast(type).arguments());
+      // Type may be a TypeRef.
+      const auto& args = TypeArguments::Handle(type.arguments());
       const auto& arg0 = AbstractType::Handle(args.TypeAt(0));
       return !recurse || CanPotentiallyBeSmi(arg0, /*recurse=*/true);
     }
diff --git a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
index 869d90c..4b33c48 100644
--- a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
+++ b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
@@ -2644,19 +2644,11 @@
 
   const DirectCallMetadata direct_call =
       direct_call_metadata_helper_.GetDirectTargetForPropertySet(offset);
-  const CallSiteAttributesMetadata call_site_attributes =
-      call_site_attributes_metadata_helper_.GetCallSiteAttributes(offset);
   const InferredTypeMetadata inferred_type =
       inferred_type_metadata_helper_.GetInferredType(offset);
 
   // True if callee can skip argument type checks.
-  bool is_unchecked_call = inferred_type.IsSkipCheck();
-  if (call_site_attributes.receiver_type != nullptr &&
-      call_site_attributes.receiver_type->HasTypeClass() &&
-      !Class::Handle(call_site_attributes.receiver_type->type_class())
-           .IsGeneric()) {
-    is_unchecked_call = true;
-  }
+  const bool is_unchecked_call = inferred_type.IsSkipCheck();
 
   Fragment instructions(MakeTemp());
   LocalVariable* variable = MakeTemporary();
@@ -2664,9 +2656,6 @@
   const TokenPosition position = ReadPosition();  // read position.
   if (p != nullptr) *p = position;
 
-  if (PeekTag() == kThisExpression) {
-    is_unchecked_call = true;
-  }
   instructions += BuildExpression();  // read receiver.
 
   LocalVariable* receiver = nullptr;
@@ -2709,7 +2698,7 @@
         Array::null_array(), kNumArgsChecked, Function::null_function(),
         Function::null_function(),
         /*result_type=*/nullptr,
-        /*use_unchecked_entry=*/is_unchecked_call, &call_site_attributes);
+        /*use_unchecked_entry=*/is_unchecked_call, /*call_site_attrs=*/nullptr);
   }
 
   instructions += Drop();  // Drop result of the setter invocation.
@@ -3071,12 +3060,14 @@
   bool is_unchecked_closure_call = false;
   bool is_unchecked_call = is_invariant || result_type.IsSkipCheck();
   if (call_site_attributes.receiver_type != nullptr) {
-    if (call_site_attributes.receiver_type->IsFunctionType()) {
+    if ((tag == kMethodInvocation) &&
+        call_site_attributes.receiver_type->IsFunctionType()) {
       AlternativeReadingScope alt(&reader_);
       SkipExpression();  // skip receiver
       is_unchecked_closure_call =
           ReadNameAsMethodName().Equals(Symbols::Call());
-    } else if (call_site_attributes.receiver_type->HasTypeClass() &&
+    } else if ((tag != kDynamicInvocation) &&
+               call_site_attributes.receiver_type->HasTypeClass() &&
                !call_site_attributes.receiver_type->IsDynamicType() &&
                !Class::Handle(call_site_attributes.receiver_type->type_class())
                     .IsGeneric()) {
@@ -3111,7 +3102,7 @@
 
   // Take note of whether the invocation is against the receiver of the current
   // function: in this case, we may skip some type checks in the callee.
-  if (PeekTag() == kThisExpression) {
+  if ((PeekTag() == kThisExpression) && (tag != kDynamicInvocation)) {
     is_unchecked_call = true;
   }
   instructions += BuildExpression();  // read receiver.
@@ -3325,12 +3316,10 @@
 
   const InferredTypeMetadata result_type =
       inferred_type_metadata_helper_.GetInferredType(offset);
-  const CallSiteAttributesMetadata call_site_attributes =
-      call_site_attributes_metadata_helper_.GetCallSiteAttributes(offset);
 
+  RELEASE_ASSERT((function_access_kind == FunctionAccessKind::kFunction) ||
+                 (function_access_kind == FunctionAccessKind::kFunctionType));
   const bool is_unchecked_closure_call =
-      ((call_site_attributes.receiver_type != nullptr) &&
-       call_site_attributes.receiver_type->IsFunctionType()) ||
       (function_access_kind == FunctionAccessKind::kFunctionType);
   Fragment instructions;
 
@@ -3383,7 +3372,7 @@
         position, Symbols::DynamicCall(), Token::kILLEGAL, type_args_len,
         argument_count, argument_names, 1, Function::null_function(),
         Function::null_function(), &result_type,
-        /*use_unchecked_entry=*/false, &call_site_attributes,
+        /*use_unchecked_entry=*/false, /*call_site_attrs=*/nullptr,
         result_type.ReceiverNotInt());
   }
   instructions += DropTempsPreserveTop(1);
diff --git a/runtime/vm/compiler/frontend/kernel_to_il.cc b/runtime/vm/compiler/frontend/kernel_to_il.cc
index ab5f8aa..0261e82 100644
--- a/runtime/vm/compiler/frontend/kernel_to_il.cc
+++ b/runtime/vm/compiler/frontend/kernel_to_il.cc
@@ -1815,6 +1815,9 @@
       type_param = dart_function.TypeParameterAt(i);
     }
     ASSERT(type_param.IsFinalized());
+    if (bound.IsTypeRef()) {
+      bound = TypeRef::Cast(bound).type();
+    }
     check_bounds +=
         AssertSubtype(TokenPosition::kNoSource, type_param, bound, name);
   }
@@ -4507,6 +4510,10 @@
   if (!type.IsNonNullable()) {
     return false;
   }
+  if (type.IsTypeRef()) {
+    return NeedsNullAssertion(
+        AbstractType::Handle(Z, TypeRef::Cast(type).type()));
+  }
   if (type.IsTypeParameter()) {
     return NeedsNullAssertion(
         AbstractType::Handle(Z, TypeParameter::Cast(type).bound()));
diff --git a/runtime/vm/compiler/frontend/kernel_translation_helper.cc b/runtime/vm/compiler/frontend/kernel_translation_helper.cc
index e6c3f73..d16f0cc 100644
--- a/runtime/vm/compiler/frontend/kernel_translation_helper.cc
+++ b/runtime/vm/compiler/frontend/kernel_translation_helper.cc
@@ -3038,6 +3038,7 @@
       zone_(translation_helper_.zone()),
       result_(AbstractType::Handle(translation_helper_.zone())),
       finalize_(finalize),
+      refers_to_derived_type_param_(false),
       apply_canonical_type_erasure_(apply_canonical_type_erasure),
       in_constant_context_(in_constant_context) {}
 
@@ -3094,6 +3095,10 @@
       break;
     case kTypeParameterType:
       BuildTypeParameterType();
+      if (result_.IsTypeParameter() &&
+          TypeParameter::Cast(result_).bound() == AbstractType::null()) {
+        refers_to_derived_type_param_ = true;
+      }
       break;
     default:
       helper_->ReportUnexpectedTag("type", tag);
@@ -3475,8 +3480,14 @@
     TypeParameterHelper helper(helper_);
     helper.ReadUntilExcludingAndSetJustRead(TypeParameterHelper::kBound);
 
+    bool saved_refers_to_derived_type_param = refers_to_derived_type_param_;
+    refers_to_derived_type_param_ = false;
     AbstractType& bound = BuildTypeWithoutFinalization();  // read ith bound.
     ASSERT(!bound.IsNull());
+    if (refers_to_derived_type_param_) {
+      bound = TypeRef::New(bound);
+    }
+    refers_to_derived_type_param_ = saved_refers_to_derived_type_param;
     type_parameters.SetBoundAt(i, bound);
     helper.ReadUntilExcludingAndSetJustRead(TypeParameterHelper::kDefaultType);
     AbstractType& default_arg = BuildTypeWithoutFinalization();
@@ -3503,6 +3514,7 @@
             derived.index() >= offset &&
             derived.index() < offset + type_parameter_count))) {
         bound = type_parameters.BoundAt(derived.index() - offset);
+        ASSERT(!bound.IsNull());
         derived.set_bound(bound);
       }
     }
diff --git a/runtime/vm/compiler/frontend/kernel_translation_helper.h b/runtime/vm/compiler/frontend/kernel_translation_helper.h
index 7dc0384..c8e3ca3 100644
--- a/runtime/vm/compiler/frontend/kernel_translation_helper.h
+++ b/runtime/vm/compiler/frontend/kernel_translation_helper.h
@@ -1538,6 +1538,7 @@
   Zone* zone_;
   AbstractType& result_;
   bool finalize_;
+  bool refers_to_derived_type_param_;
   const bool apply_canonical_type_erasure_;
   const bool in_constant_context_;
 
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index 29a2fba..4017d9b 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -19562,6 +19562,10 @@
     return false;
   }
 
+  if (IsTypeRef()) {
+    return AbstractType::Handle(zone, TypeRef::Cast(*this).type())
+        .IsStrictlyNonNullable();
+  }
   if (IsTypeParameter()) {
     const auto& bound =
         AbstractType::Handle(zone, TypeParameter::Cast(*this).bound());
@@ -19911,10 +19915,12 @@
   const TypeArguments& args = TypeArguments::Handle(zone, arguments());
   const intptr_t num_args = args.IsNull() ? 0 : args.Length();
   intptr_t first_type_param_index;
-  intptr_t num_type_params;  // Number of type parameters to print.
+  intptr_t num_type_params = num_args;  // Number of type parameters to print.
   cls = type_class();
-  // Do not print the full vector, but only the declared type parameters.
-  num_type_params = cls.NumTypeParameters();
+  if (cls.is_declaration_loaded()) {
+    // Do not print the full vector, but only the declared type parameters.
+    num_type_params = cls.NumTypeParameters();
+  }
   printer->AddString(cls.NameCString(name_visibility));
   if (num_type_params > num_args) {
     first_type_param_index = 0;
@@ -20055,6 +20061,7 @@
 }
 
 AbstractTypePtr AbstractType::UnwrapFutureOr() const {
+  // Works properly for a TypeRef without dereferencing it.
   if (!IsFutureOrType()) {
     return ptr();
   }
@@ -20982,8 +20989,13 @@
   if (num_type_params > 0) {
     const TypeParameters& type_params =
         TypeParameters::Handle(type_parameters());
-    const TypeArguments& bounds = TypeArguments::Handle(type_params.bounds());
-    result = CombineHashes(result, bounds.Hash());
+    // Do not calculate the hash of the bounds using TypeArguments::Hash(),
+    // because HashForRange() dereferences TypeRefs which should not be here.
+    AbstractType& bound = AbstractType::Handle();
+    for (intptr_t i = 0; i < num_type_params; i++) {
+      bound = type_params.BoundAt(i);
+      result = CombineHashes(result, bound.Hash());
+    }
     // Since the default arguments are ignored when comparing two generic
     // function types for type equality, the hash does not depend on them.
   }
@@ -21705,7 +21717,9 @@
         upper_bound = upper_bound.InstantiateFrom(
             instantiator_type_arguments, function_type_arguments,
             num_free_fun_type_params, space, trail);
-        if (upper_bound.ptr() == Type::NeverType()) {
+        if ((upper_bound.IsTypeRef() &&
+             TypeRef::Cast(upper_bound).type() == Type::NeverType()) ||
+            (upper_bound.ptr() == Type::NeverType())) {
           // Normalize 'X extends Never' to 'Never'.
           result = Type::NeverType();
         } else if (upper_bound.ptr() != bound()) {
diff --git a/tests/language/regress/regress46276_test.dart b/tests/language/regress/regress46276_test.dart
new file mode 100644
index 0000000..cc168d2
--- /dev/null
+++ b/tests/language/regress/regress46276_test.dart
@@ -0,0 +1,15 @@
+// Copyright (c) 2021, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+abstract class Base<A, B> {}
+
+extension on Object {
+  B? foo<A extends Base<A, B>, B extends Base<A, B>>(B? orig) {
+    return null;
+  }
+}
+
+main() {
+  print(Object().foo);
+}
diff --git a/tests/standalone/io/socket_sigpipe_test.dart b/tests/standalone/io/socket_sigpipe_test.dart
new file mode 100644
index 0000000..ec6f5c8
--- /dev/null
+++ b/tests/standalone/io/socket_sigpipe_test.dart
@@ -0,0 +1,74 @@
+// Copyright (c) 2021, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+//
+// Tests that SIGPIPE won't terminate websocket client dart app.
+
+import 'dart:async';
+import 'dart:convert';
+import 'dart:ffi';
+import 'dart:io';
+
+import "package:async_helper/async_helper.dart";
+import "package:expect/expect.dart";
+import 'package:ffi/ffi.dart';
+import 'package:path/path.dart' as p;
+
+import '../../../tests/ffi/dylib_utils.dart';
+
+class Isolate extends Opaque {}
+
+abstract class FfiBindings {
+  static final ffiTestFunctions = dlopenPlatformSpecific("ffi_test_functions");
+
+  static final RestoreSIGPIPEHandler =
+      ffiTestFunctions.lookupFunction<Void Function(), void Function()>(
+          "RestoreSIGPIPEHandler");
+  static final SleepOnAnyOS = ffiTestFunctions.lookupFunction<
+      Void Function(IntPtr), void Function(int)>("SleepOnAnyOS");
+}
+
+Future<void> main() async {
+  asyncStart();
+
+  final server = await Process.start(Platform.executable, <String>[
+    p.join(p.dirname(Platform.script.toFilePath()),
+        "socket_sigpipe_test_server.dart")
+  ]);
+  final serverPort = Completer<int>();
+  server.stdout
+      .transform(utf8.decoder)
+      .transform(LineSplitter())
+      .listen((line) {
+    print('server stdout: $line');
+    if (!serverPort.isCompleted) {
+      serverPort.complete(int.parse(line));
+    }
+  });
+  server.stderr
+      .transform(utf8.decoder)
+      .transform(LineSplitter())
+      .listen((data) {
+    print('server stderr: $data');
+  });
+
+  FfiBindings.RestoreSIGPIPEHandler();
+  final ws =
+      await WebSocket.connect('ws://localhost:${await serverPort.future}');
+  ws.listen((var data) {
+    print('Got $data');
+    // Sleep to prevent closed socket events coming through and being handled.
+    // This way websocket stays open and writing into it should trigger SIGPIPE.
+    // Unless of course we requested SIGPIPE not to be generated on broken socket
+    // pipe. This is what this test is testing - that the SIGPIPE is not generated
+    // on broken socket pipe.
+    ws.add('foo');
+    FfiBindings.SleepOnAnyOS(10 /*seconds*/); // give server time to exit
+    ws.add('baz');
+    ws.close();
+  }, onDone: () {
+    asyncEnd();
+  }, onError: (e, st) {
+    Expect.fail('Client websocket failed $e $st');
+  });
+}
diff --git a/tests/standalone/io/socket_sigpipe_test_server.dart b/tests/standalone/io/socket_sigpipe_test_server.dart
new file mode 100644
index 0000000..89265af
--- /dev/null
+++ b/tests/standalone/io/socket_sigpipe_test_server.dart
@@ -0,0 +1,23 @@
+// Copyright (c) 2021, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+//
+// Helper server program for socket_sigpipe_test.dart
+
+import "package:expect/expect.dart";
+import "dart:async";
+import "dart:io";
+
+main() {
+  HttpServer.bind("127.0.0.1", 0).then((server) {
+    print(server.port);
+    server.listen((request) {
+      WebSocketTransformer.upgrade(request).then((websocket) async {
+        websocket.add('bar');
+        await websocket.close();
+        await server.close();
+        print('closed');
+      });
+    });
+  });
+}
diff --git a/tests/standalone/standalone.status b/tests/standalone/standalone.status
index 6dba96d..9dc7f89 100644
--- a/tests/standalone/standalone.status
+++ b/tests/standalone/standalone.status
@@ -53,6 +53,7 @@
 [ $runtime == dart_precompiled ]
 http_launch_test: Skip
 io/addlatexhash_test: Skip
+io/socket_sigpipe_test: SkipByDesign # Spawns server process using Platform.executable
 io/wait_for_event_isolate_test: SkipByDesign # Uses mirrors.
 io/wait_for_event_microtask_test: SkipByDesign # Uses mirrors.
 io/wait_for_event_nested_microtask_test: SkipByDesign # Uses mirrors.
diff --git a/tests/standalone_2/io/socket_sigpipe_test.dart b/tests/standalone_2/io/socket_sigpipe_test.dart
new file mode 100644
index 0000000..ec6f5c8
--- /dev/null
+++ b/tests/standalone_2/io/socket_sigpipe_test.dart
@@ -0,0 +1,74 @@
+// Copyright (c) 2021, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+//
+// Tests that SIGPIPE won't terminate websocket client dart app.
+
+import 'dart:async';
+import 'dart:convert';
+import 'dart:ffi';
+import 'dart:io';
+
+import "package:async_helper/async_helper.dart";
+import "package:expect/expect.dart";
+import 'package:ffi/ffi.dart';
+import 'package:path/path.dart' as p;
+
+import '../../../tests/ffi/dylib_utils.dart';
+
+class Isolate extends Opaque {}
+
+abstract class FfiBindings {
+  static final ffiTestFunctions = dlopenPlatformSpecific("ffi_test_functions");
+
+  static final RestoreSIGPIPEHandler =
+      ffiTestFunctions.lookupFunction<Void Function(), void Function()>(
+          "RestoreSIGPIPEHandler");
+  static final SleepOnAnyOS = ffiTestFunctions.lookupFunction<
+      Void Function(IntPtr), void Function(int)>("SleepOnAnyOS");
+}
+
+Future<void> main() async {
+  asyncStart();
+
+  final server = await Process.start(Platform.executable, <String>[
+    p.join(p.dirname(Platform.script.toFilePath()),
+        "socket_sigpipe_test_server.dart")
+  ]);
+  final serverPort = Completer<int>();
+  server.stdout
+      .transform(utf8.decoder)
+      .transform(LineSplitter())
+      .listen((line) {
+    print('server stdout: $line');
+    if (!serverPort.isCompleted) {
+      serverPort.complete(int.parse(line));
+    }
+  });
+  server.stderr
+      .transform(utf8.decoder)
+      .transform(LineSplitter())
+      .listen((data) {
+    print('server stderr: $data');
+  });
+
+  FfiBindings.RestoreSIGPIPEHandler();
+  final ws =
+      await WebSocket.connect('ws://localhost:${await serverPort.future}');
+  ws.listen((var data) {
+    print('Got $data');
+    // Sleep to prevent closed socket events coming through and being handled.
+    // This way websocket stays open and writing into it should trigger SIGPIPE.
+    // Unless of course we requested SIGPIPE not to be generated on broken socket
+    // pipe. This is what this test is testing - that the SIGPIPE is not generated
+    // on broken socket pipe.
+    ws.add('foo');
+    FfiBindings.SleepOnAnyOS(10 /*seconds*/); // give server time to exit
+    ws.add('baz');
+    ws.close();
+  }, onDone: () {
+    asyncEnd();
+  }, onError: (e, st) {
+    Expect.fail('Client websocket failed $e $st');
+  });
+}
diff --git a/tests/standalone_2/io/socket_sigpipe_test_server.dart b/tests/standalone_2/io/socket_sigpipe_test_server.dart
new file mode 100644
index 0000000..89265af
--- /dev/null
+++ b/tests/standalone_2/io/socket_sigpipe_test_server.dart
@@ -0,0 +1,23 @@
+// Copyright (c) 2021, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+//
+// Helper server program for socket_sigpipe_test.dart
+
+import "package:expect/expect.dart";
+import "dart:async";
+import "dart:io";
+
+main() {
+  HttpServer.bind("127.0.0.1", 0).then((server) {
+    print(server.port);
+    server.listen((request) {
+      WebSocketTransformer.upgrade(request).then((websocket) async {
+        websocket.add('bar');
+        await websocket.close();
+        await server.close();
+        print('closed');
+      });
+    });
+  });
+}
diff --git a/tests/standalone_2/standalone_2.status b/tests/standalone_2/standalone_2.status
index 1745694..2ce913c 100644
--- a/tests/standalone_2/standalone_2.status
+++ b/tests/standalone_2/standalone_2.status
@@ -53,6 +53,7 @@
 [ $runtime == dart_precompiled ]
 http_launch_test: Skip
 io/addlatexhash_test: Skip
+io/socket_sigpipe_test: SkipByDesign # Spawns server process using Platform.executable
 io/wait_for_event_isolate_test: SkipByDesign # Uses mirrors.
 io/wait_for_event_microtask_test: SkipByDesign # Uses mirrors.
 io/wait_for_event_nested_microtask_test: SkipByDesign # Uses mirrors.
diff --git a/tools/VERSION b/tools/VERSION
index 476eb74..f477762 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 14
 PATCH 0
-PRERELEASE 219
+PRERELEASE 220
 PRERELEASE_PATCH 0
\ No newline at end of file