Version 2.13.0-160.0.dev

Merge commit 'b047c6e1886b21b7f4ccdcbf91fa6e00839afe3d' into 'dev'
diff --git a/DEPS b/DEPS
index 225447d..a2adf45 100644
--- a/DEPS
+++ b/DEPS
@@ -44,7 +44,7 @@
   # co19 is a cipd package. Use update.sh in tests/co19[_2] to update these
   # hashes. It requires access to the dart-build-access group, which EngProd
   # has.
-  "co19_rev": "1abf208ef6428aac8fee6f3175d65b1c59cd15c8",
+  "co19_rev": "ae818220b12ec9c2470519db2c7167cbe4745e12",
   "co19_2_rev": "cf6eed0535e45413672bb5bb6e65df9f59846372",
 
   # The internal benchmarks to use. See go/dart-benchmarks-internal
@@ -654,8 +654,6 @@
   }
 }
 
-# TODO(iposva): Move the necessary tools so that hooks can be run
-# without the runtime being available.
 hooks = [
   {
     "name": "firefox_jsshell",
diff --git a/pkg/_fe_analyzer_shared/test/flow_analysis/flow_analysis_mini_ast.dart b/pkg/_fe_analyzer_shared/test/flow_analysis/flow_analysis_mini_ast.dart
index cae9a5f..d7fbe42 100644
--- a/pkg/_fe_analyzer_shared/test/flow_analysis/flow_analysis_mini_ast.dart
+++ b/pkg/_fe_analyzer_shared/test/flow_analysis/flow_analysis_mini_ast.dart
@@ -782,6 +782,7 @@
 class _CheckPromoted extends Statement {
   final Var variable;
   final String? expectedTypeStr;
+  final StackTrace _creationTrace = StackTrace.current;
 
   _CheckPromoted(this.variable, this.expectedTypeStr) : super._();
 
@@ -800,11 +801,7 @@
   void _visit(
       Harness h, FlowAnalysis<Node, Statement, Expression, Var, Type> flow) {
     var promotedType = flow.promotedType(variable);
-    if (expectedTypeStr == null) {
-      expect(promotedType, isNull);
-    } else {
-      expect(promotedType?.type, expectedTypeStr);
-    }
+    expect(promotedType?.type, expectedTypeStr, reason: '$_creationTrace');
   }
 }
 
diff --git a/pkg/_fe_analyzer_shared/test/flow_analysis/why_not_promoted/data/argument_type_not_assignable_nullability_error.dart b/pkg/_fe_analyzer_shared/test/flow_analysis/why_not_promoted/data/argument_type_not_assignable_nullability_error.dart
new file mode 100644
index 0000000..1f92f0d
--- /dev/null
+++ b/pkg/_fe_analyzer_shared/test/flow_analysis/why_not_promoted/data/argument_type_not_assignable_nullability_error.dart
@@ -0,0 +1,91 @@
+// 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.
+
+// This test contains a test case for each condition that can lead to the front
+// end's `ArgumentTypeNotAssignableNullability` error, for which we wish to
+// report "why not promoted" context information.
+
+class C1 {
+  int? bad;
+  f(int i) {}
+}
+
+required_unnamed(C1 c) {
+  if (c.bad == null) return;
+  c.f(
+      /*analyzer.notPromoted(propertyNotPromoted(target: member:C1.bad, type: int?))*/ c
+          . /*cfe.notPromoted(propertyNotPromoted(target: member:C1.bad, type: int?))*/ bad);
+}
+
+class C2 {
+  int? bad;
+  f([int i = 0]) {}
+}
+
+optional_unnamed(C2 c) {
+  if (c.bad == null) return;
+  c.f(
+      /*analyzer.notPromoted(propertyNotPromoted(target: member:C2.bad, type: int?))*/ c
+          . /*cfe.notPromoted(propertyNotPromoted(target: member:C2.bad, type: int?))*/ bad);
+}
+
+class C3 {
+  int? bad;
+  f({required int i}) {}
+}
+
+required_named(C3 c) {
+  if (c.bad == null) return;
+  c.f(
+      /*analyzer.notPromoted(propertyNotPromoted(target: member:C3.bad, type: int?))*/ i:
+          c. /*cfe.notPromoted(propertyNotPromoted(target: member:C3.bad, type: int?))*/ bad);
+}
+
+class C4 {
+  int? bad;
+  f({int i = 0}) {}
+}
+
+optional_named(C4 c) {
+  if (c.bad == null) return;
+  c.f(
+      /*analyzer.notPromoted(propertyNotPromoted(target: member:C4.bad, type: int?))*/ i:
+          c. /*cfe.notPromoted(propertyNotPromoted(target: member:C4.bad, type: int?))*/ bad);
+}
+
+class C5 {
+  List<int>? bad;
+  f<T>(List<T> x) {}
+}
+
+type_inferred(C5 c) {
+  if (c.bad == null) return;
+  c.f(
+      /*analyzer.notPromoted(propertyNotPromoted(target: member:C5.bad, type: List<int>?))*/ c
+          . /*cfe.notPromoted(propertyNotPromoted(target: member:C5.bad, type: List<int>?))*/ bad);
+}
+
+class C6 {
+  int? bad;
+  C6(int i);
+}
+
+C6 constructor_with_implicit_new(C6 c) {
+  if (c.bad == null) return;
+  return C6(
+      /*analyzer.notPromoted(propertyNotPromoted(target: member:C6.bad, type: int?))*/ c
+          . /*cfe.notPromoted(propertyNotPromoted(target: member:C6.bad, type: int?))*/ bad);
+}
+
+class C7 {
+  int? bad;
+  C7(int i);
+}
+
+C7 constructor_with_explicit_new(C7 c) {
+  if (c.bad == null) return;
+  return new C7(
+      /*analyzer.notPromoted(propertyNotPromoted(target: member:C7.bad, type: int?))*/ c
+          . /*cfe.notPromoted(propertyNotPromoted(target: member:C7.bad, type: int?))*/ bad);
+}
diff --git a/pkg/analysis_server/lib/src/context_manager.dart b/pkg/analysis_server/lib/src/context_manager.dart
index b0164f2..9f4fee7 100644
--- a/pkg/analysis_server/lib/src/context_manager.dart
+++ b/pkg/analysis_server/lib/src/context_manager.dart
@@ -15,7 +15,6 @@
 import 'package:analyzer/src/dart/analysis/driver.dart';
 import 'package:analyzer/src/dart/analysis/driver_based_analysis_context.dart';
 import 'package:analyzer/src/dart/analysis/performance_logger.dart';
-import 'package:analyzer/src/generated/engine.dart';
 import 'package:analyzer/src/generated/java_engine.dart';
 import 'package:analyzer/src/generated/sdk.dart';
 import 'package:analyzer/src/generated/source.dart';
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/completion_manager.dart b/pkg/analysis_server/lib/src/services/completion/dart/completion_manager.dart
index 5f512c5..245abf6 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/completion_manager.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/completion_manager.dart
@@ -3,8 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 
 import 'package:analysis_server/src/protocol_server.dart';
-import 'package:analysis_server/src/provisional/completion/completion_core.dart'
-    show AbortCompletion, CompletionRequest;
 import 'package:analysis_server/src/provisional/completion/completion_core.dart';
 import 'package:analysis_server/src/provisional/completion/dart/completion_dart.dart';
 import 'package:analysis_server/src/services/completion/completion_core.dart';
diff --git a/pkg/analysis_server/test/integration/server/bazel_changes_test.dart b/pkg/analysis_server/test/integration/server/bazel_changes_test.dart
index a70cd13..edca9a7 100644
--- a/pkg/analysis_server/test/integration/server/bazel_changes_test.dart
+++ b/pkg/analysis_server/test/integration/server/bazel_changes_test.dart
@@ -132,7 +132,7 @@
     // error again.
     await resetCompleterAndErrors();
     writeFile(generatedFilePath, 'different_fun() {}');
-    writeFile(commandLogPath, 'Build completed successfully');
+    writeFile(commandLogPath, 'Build completed');
 
     await processedNotification.future;
     expect(errors, isNotEmpty);
@@ -140,7 +140,7 @@
     // Now delete the file completely.
     await resetCompleterAndErrors();
     File(generatedFilePath).deleteSync();
-    writeFile(commandLogPath, 'Build completed successfully');
+    writeFile(commandLogPath, 'Build did NOT complete successfully');
 
     await processedNotification.future;
     expect(errors, isNotEmpty);
diff --git a/pkg/analyzer/analysis_options.yaml b/pkg/analyzer/analysis_options.yaml
index ed9332f..9996e7f 100644
--- a/pkg/analyzer/analysis_options.yaml
+++ b/pkg/analyzer/analysis_options.yaml
@@ -27,6 +27,7 @@
 linter:
   rules:
     - always_use_package_imports
+    - avoid_dynamic_calls
     - avoid_unused_constructor_parameters
     - await_only_futures
     - empty_statements
diff --git a/pkg/analyzer/lib/src/dart/analysis/driver.dart b/pkg/analyzer/lib/src/dart/analysis/driver.dart
index 0703381..3cec775 100644
--- a/pkg/analyzer/lib/src/dart/analysis/driver.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/driver.dart
@@ -80,7 +80,7 @@
 /// TODO(scheglov) Clean up the list of implicitly analyzed files.
 class AnalysisDriver implements AnalysisDriverGeneric {
   /// The version of data format, should be incremented on every format change.
-  static const int DATA_VERSION = 132;
+  static const int DATA_VERSION = 133;
 
   /// The length of the list returned by [_computeDeclaredVariablesSignature].
   static const int _declaredVariablesSignatureLength = 4;
diff --git a/pkg/analyzer/lib/src/dart/ast/constant_evaluator.dart b/pkg/analyzer/lib/src/dart/ast/constant_evaluator.dart
index f2e5968..ae49c26 100644
--- a/pkg/analyzer/lib/src/dart/ast/constant_evaluator.dart
+++ b/pkg/analyzer/lib/src/dart/ast/constant_evaluator.dart
@@ -179,6 +179,14 @@
         if (leftOperand is int && rightOperand is int) {
           return leftOperand >> rightOperand;
         }
+      } else if (node.operator.type == TokenType.GT_GT_GT) {
+        if (leftOperand is int && rightOperand is int) {
+          // TODO(srawlins): Replace with native VM implementation once stable.
+          return rightOperand >= 64
+              ? 0
+              : (leftOperand >> rightOperand) &
+                  ((1 << (64 - rightOperand)) - 1);
+        }
       } else if (node.operator.type == TokenType.LT) {
         // numeric or {@code null}
         if (leftOperand is num && rightOperand is num) {
diff --git a/pkg/analyzer/lib/src/dart/constant/value.dart b/pkg/analyzer/lib/src/dart/constant/value.dart
index ca4a5a1..6bb7701 100644
--- a/pkg/analyzer/lib/src/dart/constant/value.dart
+++ b/pkg/analyzer/lib/src/dart/constant/value.dart
@@ -2063,21 +2063,13 @@
       var rightValue = rightOperand.value;
       if (rightValue == null) {
         return UNKNOWN_VALUE;
-      } else if (rightValue.bitLength > 31) {
-        return UNKNOWN_VALUE;
-      }
-      if (rightValue >= 0) {
-        // TODO(brianwilkerson) After the analyzer package has a minimum SDK
-        // constraint that includes support for the real operator, consider
-        // changing the line below to
-        //   return new IntState(value >>> rightValue);
-        int divisor = 1 << rightValue;
-        if (divisor == 0) {
-          // The `rightValue` is large enough to cause all of the non-zero bits
-          // in the left operand to be shifted out of the value.
-          return IntState(0);
-        }
-        return IntState(value! ~/ divisor);
+      } else if (rightValue >= 64) {
+        return IntState(0);
+      } else if (rightValue >= 0) {
+        // TODO(srawlins): Replace with real operator once stable, like:
+        //     return new IntState(value >>> rightValue);
+        return IntState(
+            (value! >> rightValue) & ((1 << (64 - rightValue)) - 1));
       }
     }
     throw EvaluationException(CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION);
diff --git a/pkg/analyzer/lib/src/dart/resolver/annotation_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/annotation_resolver.dart
index 39fe163..2ffd93d 100644
--- a/pkg/analyzer/lib/src/dart/resolver/annotation_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/annotation_resolver.dart
@@ -2,6 +2,7 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE 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';
@@ -28,11 +29,12 @@
   bool get _genericMetadataIsEnabled =>
       _definingLibrary.featureSet.isEnabled(Feature.generic_metadata);
 
-  void resolve(AnnotationImpl node) {
+  void resolve(AnnotationImpl node,
+      List<Map<DartType, NonPromotionReason> Function()> whyNotPromotedInfo) {
     AstNode parent = node.parent;
 
     node.typeArguments?.accept(_resolver);
-    _resolve(node);
+    _resolve(node, whyNotPromotedInfo);
 
     var elementAnnotationImpl =
         node.elementAnnotation as ElementAnnotationImpl?;
@@ -48,6 +50,7 @@
     AnnotationImpl node,
     ClassElement classElement,
     SimpleIdentifierImpl? getterName,
+    List<Map<DartType, NonPromotionReason> Function()> whyNotPromotedInfo,
   ) {
     ExecutableElement? getter;
     if (getterName != null) {
@@ -63,7 +66,7 @@
     node.element = getter;
 
     if (getterName != null && getter is PropertyAccessorElement) {
-      _propertyAccessorElement(node, getterName, getter);
+      _propertyAccessorElement(node, getterName, getter, whyNotPromotedInfo);
       _resolveAnnotationElementGetter(node, getter);
     } else if (getter is! ConstructorElement) {
       _errorReporter.reportErrorForNode(
@@ -72,7 +75,7 @@
       );
     }
 
-    node.arguments?.accept(_resolver);
+    _visitArguments(node, whyNotPromotedInfo);
   }
 
   void _constructorInvocation(
@@ -80,6 +83,7 @@
     ClassElement classElement,
     SimpleIdentifierImpl? constructorName,
     ArgumentList argumentList,
+    List<Map<DartType, NonPromotionReason> Function()> whyNotPromotedInfo,
   ) {
     ConstructorElement? constructorElement;
     if (constructorName != null) {
@@ -99,7 +103,8 @@
         CompileTimeErrorCode.INVALID_ANNOTATION,
         node,
       );
-      argumentList.accept(_resolver);
+      _resolver.visitArgumentList(argumentList,
+          whyNotPromotedInfo: whyNotPromotedInfo);
       return;
     }
 
@@ -109,7 +114,8 @@
     if (typeParameters.isEmpty) {
       _resolveConstructorInvocationArguments(node);
       InferenceContext.setType(argumentList, constructorElement.type);
-      argumentList.accept(_resolver);
+      _resolver.visitArgumentList(argumentList,
+          whyNotPromotedInfo: whyNotPromotedInfo);
       return;
     }
 
@@ -127,7 +133,8 @@
       _resolveConstructorInvocationArguments(node);
 
       InferenceContext.setType(argumentList, constructorElement.type);
-      argumentList.accept(_resolver);
+      _resolver.visitArgumentList(argumentList,
+          whyNotPromotedInfo: whyNotPromotedInfo);
     }
 
     if (!_genericMetadataIsEnabled) {
@@ -156,7 +163,8 @@
       return;
     }
 
-    argumentList.accept(_resolver);
+    _resolver.visitArgumentList(argumentList,
+        whyNotPromotedInfo: whyNotPromotedInfo);
 
     var constructorRawType = _resolver.typeAnalyzer
         .constructorToGenericFunctionType(constructorElement);
@@ -186,6 +194,7 @@
     AnnotationImpl node,
     ExtensionElement extensionElement,
     SimpleIdentifierImpl? getterName,
+    List<Map<DartType, NonPromotionReason> Function()> whyNotPromotedInfo,
   ) {
     ExecutableElement? getter;
     if (getterName != null) {
@@ -197,7 +206,7 @@
     node.element = getter;
 
     if (getterName != null && getter is PropertyAccessorElement) {
-      _propertyAccessorElement(node, getterName, getter);
+      _propertyAccessorElement(node, getterName, getter, whyNotPromotedInfo);
       _resolveAnnotationElementGetter(node, getter);
     } else {
       _errorReporter.reportErrorForNode(
@@ -206,23 +215,25 @@
       );
     }
 
-    node.arguments?.accept(_resolver);
+    _visitArguments(node, whyNotPromotedInfo);
   }
 
   void _propertyAccessorElement(
     AnnotationImpl node,
     SimpleIdentifierImpl name,
     PropertyAccessorElement element,
+    List<Map<DartType, NonPromotionReason> Function()> whyNotPromotedInfo,
   ) {
     element = _resolver.toLegacyElement(element);
     name.staticElement = element;
     node.element = element;
 
     _resolveAnnotationElementGetter(node, element);
-    node.arguments?.accept(_resolver);
+    _visitArguments(node, whyNotPromotedInfo);
   }
 
-  void _resolve(AnnotationImpl node) {
+  void _resolve(AnnotationImpl node,
+      List<Map<DartType, NonPromotionReason> Function()> whyNotPromotedInfo) {
     SimpleIdentifierImpl name1;
     SimpleIdentifierImpl? name2;
     SimpleIdentifierImpl? name3;
@@ -246,23 +257,24 @@
         node,
         [name1.name],
       );
-      node.arguments?.accept(_resolver);
+      _visitArguments(node, whyNotPromotedInfo);
       return;
     }
 
     // Class(args) or Class.CONST
     if (element1 is ClassElement) {
       if (argumentList != null) {
-        _constructorInvocation(node, element1, name2, argumentList);
+        _constructorInvocation(
+            node, element1, name2, argumentList, whyNotPromotedInfo);
       } else {
-        _classGetter(node, element1, name2);
+        _classGetter(node, element1, name2, whyNotPromotedInfo);
       }
       return;
     }
 
     // Extension.CONST
     if (element1 is ExtensionElement) {
-      _extensionGetter(node, element1, name2);
+      _extensionGetter(node, element1, name2, whyNotPromotedInfo);
       return;
     }
 
@@ -274,20 +286,21 @@
         // prefix.Class(args) or prefix.Class.CONST
         if (element2 is ClassElement) {
           if (argumentList != null) {
-            _constructorInvocation(node, element2, name3, argumentList);
+            _constructorInvocation(
+                node, element2, name3, argumentList, whyNotPromotedInfo);
           } else {
-            _classGetter(node, element2, name3);
+            _classGetter(node, element2, name3, whyNotPromotedInfo);
           }
           return;
         }
         // prefix.Extension.CONST
         if (element2 is ExtensionElement) {
-          _extensionGetter(node, element2, name3);
+          _extensionGetter(node, element2, name3, whyNotPromotedInfo);
           return;
         }
         // prefix.CONST
         if (element2 is PropertyAccessorElement) {
-          _propertyAccessorElement(node, name2, element2);
+          _propertyAccessorElement(node, name2, element2, whyNotPromotedInfo);
           return;
         }
         // undefined
@@ -297,7 +310,7 @@
             node,
             [name2.name],
           );
-          node.arguments?.accept(_resolver);
+          _visitArguments(node, whyNotPromotedInfo);
           return;
         }
       }
@@ -305,7 +318,7 @@
 
     // CONST
     if (element1 is PropertyAccessorElement) {
-      _propertyAccessorElement(node, name1, element1);
+      _propertyAccessorElement(node, name1, element1, whyNotPromotedInfo);
       return;
     }
 
@@ -319,7 +332,7 @@
       node,
     );
 
-    node.arguments?.accept(_resolver);
+    _visitArguments(node, whyNotPromotedInfo);
   }
 
   void _resolveAnnotationElementGetter(
@@ -379,4 +392,13 @@
       }
     }
   }
+
+  void _visitArguments(AnnotationImpl node,
+      List<Map<DartType, NonPromotionReason> Function()> whyNotPromotedInfo) {
+    var arguments = node.arguments;
+    if (arguments != null) {
+      _resolver.visitArgumentList(arguments,
+          whyNotPromotedInfo: whyNotPromotedInfo);
+    }
+  }
 }
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 6617ab2..a515061 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,6 +2,7 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE 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';
@@ -35,11 +36,12 @@
   NullableDereferenceVerifier get _nullableDereferenceVerifier =>
       _resolver.nullableDereferenceVerifier;
 
-  void resolve(FunctionExpressionInvocationImpl node) {
+  void resolve(FunctionExpressionInvocationImpl node,
+      List<Map<DartType, NonPromotionReason> Function()> whyNotPromotedInfo) {
     var function = node.function;
 
     if (function is ExtensionOverrideImpl) {
-      _resolveReceiverExtensionOverride(node, function);
+      _resolveReceiverExtensionOverride(node, function, whyNotPromotedInfo);
       return;
     }
 
@@ -49,12 +51,13 @@
       // `_nullableDereferenceVerifier.expression` because
       // `_resolveReceiverInterfaceType` calls `TypePropertyResolver.resolve`,
       // which does the necessary null checking.
-      _resolveReceiverInterfaceType(node, function, receiverType);
+      _resolveReceiverInterfaceType(
+          node, function, receiverType, whyNotPromotedInfo);
       return;
     }
 
     if (_checkForUseOfVoidResult(function, receiverType)) {
-      _unresolved(node, DynamicTypeImpl.instance);
+      _unresolved(node, DynamicTypeImpl.instance, whyNotPromotedInfo);
       return;
     }
 
@@ -62,18 +65,18 @@
         errorCode: CompileTimeErrorCode.UNCHECKED_INVOCATION_OF_NULLABLE_VALUE);
 
     if (receiverType is FunctionType) {
-      _resolve(node, receiverType);
+      _resolve(node, receiverType, whyNotPromotedInfo);
       return;
     }
 
     if (identical(receiverType, NeverTypeImpl.instance)) {
       _errorReporter.reportErrorForNode(
           HintCode.RECEIVER_OF_TYPE_NEVER, function);
-      _unresolved(node, NeverTypeImpl.instance);
+      _unresolved(node, NeverTypeImpl.instance, whyNotPromotedInfo);
       return;
     }
 
-    _unresolved(node, DynamicTypeImpl.instance);
+    _unresolved(node, DynamicTypeImpl.instance, whyNotPromotedInfo);
   }
 
   /// Check for situations where the result of a method or function is used,
@@ -100,10 +103,12 @@
     return true;
   }
 
-  void _resolve(FunctionExpressionInvocationImpl node, FunctionType rawType) {
+  void _resolve(FunctionExpressionInvocationImpl node, FunctionType rawType,
+      List<Map<DartType, NonPromotionReason> Function()> whyNotPromotedInfo) {
     _inferenceHelper.resolveFunctionExpressionInvocation(
       node: node,
       rawType: rawType,
+      whyNotPromotedInfo: whyNotPromotedInfo,
     );
 
     var returnType = _inferenceHelper.computeInvokeReturnType(
@@ -112,13 +117,16 @@
     _inferenceHelper.recordStaticType(node, returnType);
   }
 
-  void _resolveArguments(FunctionExpressionInvocationImpl node) {
-    node.argumentList.accept(_resolver);
+  void _resolveArguments(FunctionExpressionInvocationImpl node,
+      List<Map<DartType, NonPromotionReason> Function()> whyNotPromotedInfo) {
+    _resolver.visitArgumentList(node.argumentList,
+        whyNotPromotedInfo: whyNotPromotedInfo);
   }
 
   void _resolveReceiverExtensionOverride(
     FunctionExpressionInvocationImpl node,
     ExtensionOverride function,
+    List<Map<DartType, NonPromotionReason> Function()> whyNotPromotedInfo,
   ) {
     var result = _extensionResolver.getOverrideMember(
       function,
@@ -133,7 +141,7 @@
         function,
         [function.extensionName.name],
       );
-      return _unresolved(node, DynamicTypeImpl.instance);
+      return _unresolved(node, DynamicTypeImpl.instance, whyNotPromotedInfo);
     }
 
     if (callElement.isStatic) {
@@ -144,13 +152,14 @@
     }
 
     var rawType = callElement.type;
-    _resolve(node, rawType);
+    _resolve(node, rawType, whyNotPromotedInfo);
   }
 
   void _resolveReceiverInterfaceType(
     FunctionExpressionInvocationImpl node,
     Expression function,
     InterfaceType receiverType,
+    List<Map<DartType, NonPromotionReason> Function()> whyNotPromotedInfo,
   ) {
     var result = _typePropertyResolver.resolve(
       receiver: function,
@@ -168,7 +177,7 @@
           function,
         );
       }
-      _unresolved(node, DynamicTypeImpl.instance);
+      _unresolved(node, DynamicTypeImpl.instance, whyNotPromotedInfo);
       return;
     }
 
@@ -177,18 +186,19 @@
         CompileTimeErrorCode.INVOCATION_OF_NON_FUNCTION_EXPRESSION,
         function,
       );
-      _unresolved(node, DynamicTypeImpl.instance);
+      _unresolved(node, DynamicTypeImpl.instance, whyNotPromotedInfo);
       return;
     }
 
     node.staticElement = callElement;
     var rawType = callElement.type;
-    _resolve(node, rawType);
+    _resolve(node, rawType, whyNotPromotedInfo);
   }
 
-  void _unresolved(FunctionExpressionInvocationImpl node, DartType type) {
+  void _unresolved(FunctionExpressionInvocationImpl node, DartType type,
+      List<Map<DartType, NonPromotionReason> Function()> whyNotPromotedInfo) {
     _setExplicitTypeArgumentTypes(node);
-    _resolveArguments(node);
+    _resolveArguments(node, whyNotPromotedInfo);
     node.staticInvokeType = DynamicTypeImpl.instance;
     node.staticType = type;
   }
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 461b264..865c8dd 100644
--- a/pkg/analyzer/lib/src/dart/resolver/invocation_inference_helper.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/invocation_inference_helper.dart
@@ -2,6 +2,7 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE 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';
@@ -207,6 +208,8 @@
   void resolveFunctionExpressionInvocation({
     required FunctionExpressionInvocationImpl node,
     required FunctionType rawType,
+    required List<Map<DartType, NonPromotionReason> Function()>
+        whyNotPromotedInfo,
   }) {
     _resolveInvocation(
       rawType: rawType,
@@ -215,6 +218,7 @@
       contextType: InferenceContext.getContext(node),
       isConst: false,
       errorNode: node.function,
+      whyNotPromotedInfo: whyNotPromotedInfo,
     );
 
     node.typeArgumentTypes = _typeArgumentTypes;
@@ -229,6 +233,8 @@
   void resolveMethodInvocation({
     required MethodInvocationImpl node,
     required FunctionType rawType,
+    required List<Map<DartType, NonPromotionReason> Function()>
+        whyNotPromotedInfo,
   }) {
     _resolveInvocation(
       rawType: rawType,
@@ -237,6 +243,7 @@
       contextType: InferenceContext.getContext(node),
       isConst: false,
       errorNode: node.function,
+      whyNotPromotedInfo: whyNotPromotedInfo,
     );
 
     node.typeArgumentTypes = _typeArgumentTypes;
@@ -325,9 +332,11 @@
     return false;
   }
 
-  void _resolveArguments(ArgumentList argumentList) {
+  void _resolveArguments(ArgumentList argumentList,
+      List<Map<DartType, NonPromotionReason> Function()> whyNotPromotedInfo) {
     _resolver.visitArgumentList(argumentList,
-        isIdentical: _isCallToIdentical(argumentList.parent));
+        isIdentical: _isCallToIdentical(argumentList.parent),
+        whyNotPromotedInfo: whyNotPromotedInfo);
   }
 
   void _resolveInvocation({
@@ -337,12 +346,15 @@
     required ArgumentListImpl argumentList,
     required bool isConst,
     required AstNode errorNode,
+    required List<Map<DartType, NonPromotionReason> Function()>
+        whyNotPromotedInfo,
   }) {
     if (typeArgumentList != null) {
       _resolveInvocationWithTypeArguments(
         rawType: rawType,
         typeArgumentList: typeArgumentList,
         argumentList: argumentList,
+        whyNotPromotedInfo: whyNotPromotedInfo,
       );
     } else {
       _resolveInvocationWithoutTypeArguments(
@@ -351,6 +363,7 @@
         argumentList: argumentList,
         isConst: isConst,
         errorNode: errorNode,
+        whyNotPromotedInfo: whyNotPromotedInfo,
       );
     }
     _setCorrespondingParameters(argumentList, _invokeType!);
@@ -362,12 +375,14 @@
     required ArgumentList argumentList,
     required bool isConst,
     required AstNode errorNode,
+    required List<Map<DartType, NonPromotionReason> Function()>
+        whyNotPromotedInfo,
   }) {
     var typeParameters = rawType.typeFormals;
 
     if (typeParameters.isEmpty) {
       InferenceContext.setType(argumentList, rawType);
-      _resolveArguments(argumentList);
+      _resolveArguments(argumentList, whyNotPromotedInfo);
 
       _typeArgumentTypes = const <DartType>[];
       _invokeType = rawType;
@@ -384,7 +399,7 @@
       var downwardsInvokeType = rawType.instantiate(downwardsTypeArguments);
       InferenceContext.setType(argumentList, downwardsInvokeType);
 
-      _resolveArguments(argumentList);
+      _resolveArguments(argumentList, whyNotPromotedInfo);
 
       _typeArgumentTypes = _inferUpwards(
         rawType: rawType,
@@ -401,6 +416,8 @@
     required FunctionType rawType,
     required TypeArgumentList typeArgumentList,
     required ArgumentList argumentList,
+    required List<Map<DartType, NonPromotionReason> Function()>
+        whyNotPromotedInfo,
   }) {
     var typeParameters = rawType.typeFormals;
 
@@ -428,7 +445,7 @@
     var invokeType = rawType.instantiate(typeArguments);
     InferenceContext.setType(argumentList, invokeType);
 
-    _resolveArguments(argumentList);
+    _resolveArguments(argumentList, whyNotPromotedInfo);
 
     _typeArgumentTypes = typeArguments;
     _invokeType = invokeType;
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 c2d6338..7310f7e 100644
--- a/pkg/analyzer/lib/src/dart/resolver/method_invocation_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/method_invocation_resolver.dart
@@ -2,6 +2,7 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE 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';
@@ -80,7 +81,8 @@
 
   TypeSystemImpl get _typeSystem => _resolver.typeSystem;
 
-  void resolve(MethodInvocationImpl node) {
+  void resolve(MethodInvocationImpl node,
+      List<Map<DartType, NonPromotionReason> Function()> whyNotPromotedInfo) {
     _invocation = node;
 
     var nameNode = node.methodName;
@@ -90,14 +92,15 @@
     var receiver = node.realTarget;
 
     if (receiver == null) {
-      _resolveReceiverNull(node, nameNode, name);
+      _resolveReceiverNull(node, nameNode, name, whyNotPromotedInfo);
       return;
     }
 
     if (receiver is SimpleIdentifierImpl) {
       var receiverElement = receiver.staticElement;
       if (receiverElement is PrefixElement) {
-        _resolveReceiverPrefix(node, receiverElement, nameNode, name);
+        _resolveReceiverPrefix(
+            node, receiverElement, nameNode, name, whyNotPromotedInfo);
         return;
       }
     }
@@ -105,32 +108,34 @@
     if (receiver is IdentifierImpl) {
       var receiverElement = receiver.staticElement;
       if (receiverElement is ExtensionElement) {
-        _resolveExtensionMember(
-            node, receiver, receiverElement, nameNode, name);
+        _resolveExtensionMember(node, receiver, receiverElement, nameNode, name,
+            whyNotPromotedInfo);
         return;
       }
     }
 
     if (receiver is SuperExpressionImpl) {
-      _resolveReceiverSuper(node, receiver, nameNode, name);
+      _resolveReceiverSuper(node, receiver, nameNode, name, whyNotPromotedInfo);
       return;
     }
 
     if (receiver is ExtensionOverrideImpl) {
-      _resolveExtensionOverride(node, receiver, nameNode, name);
+      _resolveExtensionOverride(
+          node, receiver, nameNode, name, whyNotPromotedInfo);
       return;
     }
 
     if (receiver is IdentifierImpl) {
       var element = receiver.staticElement;
       if (element is ClassElement) {
-        _resolveReceiverTypeLiteral(node, element, nameNode, name);
+        _resolveReceiverTypeLiteral(
+            node, element, nameNode, name, whyNotPromotedInfo);
         return;
       } else if (element is TypeAliasElement) {
         var aliasedType = element.aliasedType;
         if (aliasedType is InterfaceType) {
           _resolveReceiverTypeLiteral(
-              node, aliasedType.element, nameNode, name);
+              node, aliasedType.element, nameNode, name, whyNotPromotedInfo);
           return;
         }
       }
@@ -139,17 +144,17 @@
     DartType receiverType = receiver.typeOrThrow;
 
     if (_typeSystem.isDynamicBounded(receiverType)) {
-      _resolveReceiverDynamicBounded(node);
+      _resolveReceiverDynamicBounded(node, whyNotPromotedInfo);
       return;
     }
 
     if (receiverType is NeverTypeImpl) {
-      _resolveReceiverNever(node, receiver, receiverType);
+      _resolveReceiverNever(node, receiver, receiverType, whyNotPromotedInfo);
       return;
     }
 
     if (receiverType is VoidType) {
-      _reportUseOfVoidType(node, receiver);
+      _reportUseOfVoidType(node, receiver, whyNotPromotedInfo);
       return;
     }
 
@@ -160,7 +165,7 @@
 
     if (_typeSystem.isFunctionBounded(receiverType)) {
       _resolveReceiverFunctionBounded(
-          node, receiver, receiverType, nameNode, name);
+          node, receiver, receiverType, nameNode, name, whyNotPromotedInfo);
       return;
     }
 
@@ -171,6 +176,7 @@
       nameNode: nameNode,
       name: name,
       receiverErrorNode: receiver,
+      whyNotPromotedInfo: whyNotPromotedInfo,
     );
   }
 
@@ -210,8 +216,10 @@
     }
   }
 
-  void _reportInvocationOfNonFunction(MethodInvocationImpl node) {
-    _setDynamicResolution(node, setNameTypeToDynamic: false);
+  void _reportInvocationOfNonFunction(MethodInvocationImpl node,
+      List<Map<DartType, NonPromotionReason> Function()> whyNotPromotedInfo) {
+    _setDynamicResolution(node,
+        setNameTypeToDynamic: false, whyNotPromotedInfo: whyNotPromotedInfo);
     _resolver.errorReporter.reportErrorForNode(
       CompileTimeErrorCode.INVOCATION_OF_NON_FUNCTION,
       node.methodName,
@@ -242,8 +250,10 @@
     MethodInvocationImpl node, {
     required String? prefix,
     required String name,
+    required List<Map<DartType, NonPromotionReason> Function()>
+        whyNotPromotedInfo,
   }) {
-    _setDynamicResolution(node);
+    _setDynamicResolution(node, whyNotPromotedInfo: whyNotPromotedInfo);
 
     if (nameScope.shouldIgnoreUndefined2(prefix: prefix, name: name)) {
       return;
@@ -257,8 +267,11 @@
   }
 
   void _reportUndefinedMethod(
-      MethodInvocationImpl node, String name, ClassElement typeReference) {
-    _setDynamicResolution(node);
+      MethodInvocationImpl node,
+      String name,
+      ClassElement typeReference,
+      List<Map<DartType, NonPromotionReason> Function()> whyNotPromotedInfo) {
+    _setDynamicResolution(node, whyNotPromotedInfo: whyNotPromotedInfo);
     _resolver.errorReporter.reportErrorForNode(
       CompileTimeErrorCode.UNDEFINED_METHOD,
       node.methodName,
@@ -266,8 +279,9 @@
     );
   }
 
-  void _reportUseOfVoidType(MethodInvocationImpl node, AstNode errorNode) {
-    _setDynamicResolution(node);
+  void _reportUseOfVoidType(MethodInvocationImpl node, AstNode errorNode,
+      List<Map<DartType, NonPromotionReason> Function()> whyNotPromotedInfo) {
+    _setDynamicResolution(node, whyNotPromotedInfo: whyNotPromotedInfo);
     _resolver.errorReporter.reportErrorForNode(
       CompileTimeErrorCode.USE_OF_VOID_RESULT,
       errorNode,
@@ -276,17 +290,20 @@
 
   /// [InvocationExpression.staticInvokeType] has been set for the [node].
   /// Use it to set context for arguments, and resolve them.
-  void _resolveArguments(MethodInvocationImpl node) {
+  void _resolveArguments(MethodInvocationImpl node,
+      List<Map<DartType, NonPromotionReason> Function()> whyNotPromotedInfo) {
     // TODO(scheglov) This is bad, don't write raw type, carry it
     _inferenceHelper.inferArgumentTypesForInvocation(
       node,
       node.methodName.staticType,
     );
-    node.argumentList.accept(_resolver);
+    _resolver.visitArgumentList(node.argumentList,
+        whyNotPromotedInfo: whyNotPromotedInfo);
   }
 
-  void _resolveArguments_finishInference(MethodInvocationImpl node) {
-    _resolveArguments(node);
+  void _resolveArguments_finishInference(MethodInvocationImpl node,
+      List<Map<DartType, NonPromotionReason> Function()> whyNotPromotedInfo) {
+    _resolveArguments(node, whyNotPromotedInfo);
 
     // TODO(scheglov) This is bad, don't put / get raw FunctionType this way.
     _inferenceHelper.inferGenericInvocationExpression(
@@ -318,8 +335,13 @@
     return null;
   }
 
-  void _resolveExtensionMember(MethodInvocationImpl node, Identifier receiver,
-      ExtensionElement extension, SimpleIdentifierImpl nameNode, String name) {
+  void _resolveExtensionMember(
+      MethodInvocationImpl node,
+      Identifier receiver,
+      ExtensionElement extension,
+      SimpleIdentifierImpl nameNode,
+      String name,
+      List<Map<DartType, NonPromotionReason> Function()> whyNotPromotedInfo) {
     var getter = extension.getGetter(name);
     if (getter != null) {
       getter = _resolver.toLegacyElement(getter);
@@ -334,11 +356,11 @@
       method = _resolver.toLegacyElement(method);
       nameNode.staticElement = method;
       _reportStaticAccessToInstanceMember(method, nameNode);
-      _setResolution(node, method.type);
+      _setResolution(node, method.type, whyNotPromotedInfo);
       return;
     }
 
-    _setDynamicResolution(node);
+    _setDynamicResolution(node, whyNotPromotedInfo: whyNotPromotedInfo);
     _resolver.errorReporter.reportErrorForNode(
       CompileTimeErrorCode.UNDEFINED_EXTENSION_METHOD,
       nameNode,
@@ -346,13 +368,17 @@
     );
   }
 
-  void _resolveExtensionOverride(MethodInvocationImpl node,
-      ExtensionOverride override, SimpleIdentifierImpl nameNode, String name) {
+  void _resolveExtensionOverride(
+      MethodInvocationImpl node,
+      ExtensionOverride override,
+      SimpleIdentifierImpl nameNode,
+      String name,
+      List<Map<DartType, NonPromotionReason> Function()> whyNotPromotedInfo) {
     var result = _extensionResolver.getOverrideMember(override, name);
     var member = _resolver.toLegacyElement(result.getter);
 
     if (member == null) {
-      _setDynamicResolution(node);
+      _setDynamicResolution(node, whyNotPromotedInfo: whyNotPromotedInfo);
       _resolver.errorReporter.reportErrorForNode(
         CompileTimeErrorCode.UNDEFINED_EXTENSION_METHOD,
         nameNode,
@@ -382,10 +408,11 @@
       return _rewriteAsFunctionExpressionInvocation(node, member.returnType);
     }
 
-    _setResolution(node, member.type);
+    _setResolution(node, member.type, whyNotPromotedInfo);
   }
 
-  void _resolveReceiverDynamicBounded(MethodInvocationImpl node) {
+  void _resolveReceiverDynamicBounded(MethodInvocationImpl node,
+      List<Map<DartType, NonPromotionReason> Function()> whyNotPromotedInfo) {
     var nameNode = node.methodName;
 
     var objectElement = _typeSystem.typeProvider.objectElement;
@@ -411,7 +438,8 @@
     }
 
     _setExplicitTypeArgumentTypes();
-    node.argumentList.accept(_resolver);
+    _resolver.visitArgumentList(node.argumentList,
+        whyNotPromotedInfo: whyNotPromotedInfo);
   }
 
   void _resolveReceiverFunctionBounded(
@@ -420,9 +448,10 @@
     DartType receiverType,
     SimpleIdentifierImpl nameNode,
     String name,
+    List<Map<DartType, NonPromotionReason> Function()> whyNotPromotedInfo,
   ) {
     if (name == FunctionElement.CALL_METHOD_NAME) {
-      _setResolution(node, receiverType);
+      _setResolution(node, receiverType, whyNotPromotedInfo);
       // TODO(scheglov) Replace this with using FunctionType directly.
       // Here was erase resolution that _setResolution() sets.
       nameNode.staticElement = null;
@@ -437,6 +466,7 @@
       nameNode: nameNode,
       name: name,
       receiverErrorNode: nameNode,
+      whyNotPromotedInfo: whyNotPromotedInfo,
     );
   }
 
@@ -444,6 +474,7 @@
     MethodInvocationImpl node,
     Expression receiver,
     DartType receiverType,
+    List<Map<DartType, NonPromotionReason> Function()> whyNotPromotedInfo,
   ) {
     _setExplicitTypeArgumentTypes();
 
@@ -457,9 +488,10 @@
         _setResolution(
           node,
           objectMember.type,
+          whyNotPromotedInfo,
         );
       } else {
-        _setDynamicResolution(node);
+        _setDynamicResolution(node, whyNotPromotedInfo: whyNotPromotedInfo);
         _resolver.nullableDereferenceVerifier.report(receiver, receiverType,
             errorCode: CompileTimeErrorCode
                 .UNCHECKED_METHOD_INVOCATION_OF_NULLABLE_VALUE);
@@ -472,7 +504,7 @@
       node.staticInvokeType = _dynamicType;
       node.staticType = NeverTypeImpl.instance;
 
-      _resolveArguments(node);
+      _resolveArguments(node, whyNotPromotedInfo);
 
       _resolver.errorReporter.reportErrorForNode(
         HintCode.RECEIVER_OF_TYPE_NEVER,
@@ -486,13 +518,16 @@
       node.staticInvokeType = _dynamicType;
       node.staticType = _dynamicType;
 
-      _resolveArguments(node);
+      _resolveArguments(node, whyNotPromotedInfo);
       return;
     }
   }
 
   void _resolveReceiverNull(
-      MethodInvocationImpl node, SimpleIdentifierImpl nameNode, String name) {
+      MethodInvocationImpl node,
+      SimpleIdentifierImpl nameNode,
+      String name,
+      List<Map<DartType, NonPromotionReason> Function()> whyNotPromotedInfo) {
     var element = nameScope.lookup(name).getter;
     if (element != null) {
       element = _resolver.toLegacyElement(element);
@@ -505,7 +540,7 @@
         return _rewriteAsFunctionExpressionInvocation(node, element.returnType);
       }
       if (element is ExecutableElement) {
-        return _setResolution(node, element.type);
+        return _setResolution(node, element.type, whyNotPromotedInfo);
       }
       if (element is VariableElement) {
         _resolver.checkReadOfNotAssignedLocalVariable(nameNode, element);
@@ -514,10 +549,10 @@
       }
       // TODO(scheglov) This is a questionable distinction.
       if (element is PrefixElement) {
-        _setDynamicResolution(node);
+        _setDynamicResolution(node, whyNotPromotedInfo: whyNotPromotedInfo);
         return _reportPrefixIdentifierNotFollowedByDot(nameNode);
       }
-      return _reportInvocationOfNonFunction(node);
+      return _reportInvocationOfNonFunction(node, whyNotPromotedInfo);
     }
 
     DartType receiverType;
@@ -530,6 +565,7 @@
         node,
         prefix: null,
         name: node.methodName.name,
+        whyNotPromotedInfo: whyNotPromotedInfo,
       );
     }
 
@@ -540,11 +576,16 @@
       nameNode: nameNode,
       name: name,
       receiverErrorNode: nameNode,
+      whyNotPromotedInfo: whyNotPromotedInfo,
     );
   }
 
-  void _resolveReceiverPrefix(MethodInvocationImpl node, PrefixElement prefix,
-      SimpleIdentifierImpl nameNode, String name) {
+  void _resolveReceiverPrefix(
+      MethodInvocationImpl node,
+      PrefixElement prefix,
+      SimpleIdentifierImpl nameNode,
+      String name,
+      List<Map<DartType, NonPromotionReason> Function()> whyNotPromotedInfo) {
     // Note: prefix?.bar is reported as an error in ElementResolver.
 
     if (name == FunctionElement.LOAD_LIBRARY_NAME) {
@@ -555,7 +596,8 @@
         element = _resolver.toLegacyElement(element);
         if (element is ExecutableElement) {
           nameNode.staticElement = element;
-          return _setResolution(node, (element as ExecutableElement).type);
+          return _setResolution(
+              node, (element as ExecutableElement).type, whyNotPromotedInfo);
         }
       }
     }
@@ -574,21 +616,26 @@
     }
 
     if (element is ExecutableElement) {
-      return _setResolution(node, element.type);
+      return _setResolution(node, element.type, whyNotPromotedInfo);
     }
 
     _reportUndefinedFunction(
       node,
       prefix: prefix.name,
       name: name,
+      whyNotPromotedInfo: whyNotPromotedInfo,
     );
   }
 
-  void _resolveReceiverSuper(MethodInvocationImpl node,
-      SuperExpression receiver, SimpleIdentifierImpl nameNode, String name) {
+  void _resolveReceiverSuper(
+      MethodInvocationImpl node,
+      SuperExpression receiver,
+      SimpleIdentifierImpl nameNode,
+      String name,
+      List<Map<DartType, NonPromotionReason> Function()> whyNotPromotedInfo) {
     var enclosingClass = _resolver.enclosingClass;
     if (SuperContext.of(receiver) != SuperContext.valid) {
-      _setDynamicResolution(node);
+      _setDynamicResolution(node, whyNotPromotedInfo: whyNotPromotedInfo);
       return;
     }
 
@@ -605,7 +652,7 @@
       if (target is PropertyAccessorElement) {
         return _rewriteAsFunctionExpressionInvocation(node, target.returnType);
       }
-      _setResolution(node, target.type);
+      _setResolution(node, target.type, whyNotPromotedInfo);
       return;
     }
 
@@ -615,7 +662,7 @@
     target = _inheritance.getInherited2(enclosingClass, _currentName!);
     if (target != null) {
       nameNode.staticElement = target;
-      _setResolution(node, target.type);
+      _setResolution(node, target.type, whyNotPromotedInfo);
 
       _resolver.errorReporter.reportErrorForNode(
           CompileTimeErrorCode.ABSTRACT_SUPER_MEMBER_REFERENCE,
@@ -625,7 +672,7 @@
     }
 
     // Nothing help, there is no target at all.
-    _setDynamicResolution(node);
+    _setDynamicResolution(node, whyNotPromotedInfo: whyNotPromotedInfo);
     _resolver.errorReporter.reportErrorForNode(
         CompileTimeErrorCode.UNDEFINED_SUPER_METHOD,
         nameNode,
@@ -639,6 +686,8 @@
     required SimpleIdentifierImpl nameNode,
     required String name,
     required Expression receiverErrorNode,
+    required List<Map<DartType, NonPromotionReason> Function()>
+        whyNotPromotedInfo,
   }) {
     var result = _resolver.typePropertyResolver.resolve(
       receiver: receiver,
@@ -663,10 +712,10 @@
       if (target is PropertyAccessorElement) {
         return _rewriteAsFunctionExpressionInvocation(node, target.returnType);
       }
-      return _setResolution(node, target.type);
+      return _setResolution(node, target.type, whyNotPromotedInfo);
     }
 
-    _setDynamicResolution(node);
+    _setDynamicResolution(node, whyNotPromotedInfo: whyNotPromotedInfo);
 
     if (!result.needsGetterError) {
       return;
@@ -688,8 +737,12 @@
     }
   }
 
-  void _resolveReceiverTypeLiteral(MethodInvocationImpl node,
-      ClassElement receiver, SimpleIdentifierImpl nameNode, String name) {
+  void _resolveReceiverTypeLiteral(
+      MethodInvocationImpl node,
+      ClassElement receiver,
+      SimpleIdentifierImpl nameNode,
+      String name,
+      List<Map<DartType, NonPromotionReason> Function()> whyNotPromotedInfo) {
     if (node.isCascaded) {
       receiver = _typeType.element;
     }
@@ -703,14 +756,14 @@
           return _rewriteAsFunctionExpressionInvocation(
               node, element.returnType);
         }
-        _setResolution(node, element.type);
+        _setResolution(node, element.type, whyNotPromotedInfo);
       } else {
-        _reportInvocationOfNonFunction(node);
+        _reportInvocationOfNonFunction(node, whyNotPromotedInfo);
       }
       return;
     }
 
-    _reportUndefinedMethod(node, name, receiver);
+    _reportUndefinedMethod(node, name, receiver, whyNotPromotedInfo);
   }
 
   /// If the given [type] is a type parameter, replace with its bound.
@@ -770,14 +823,16 @@
   }
 
   void _setDynamicResolution(MethodInvocationImpl node,
-      {bool setNameTypeToDynamic = true}) {
+      {bool setNameTypeToDynamic = true,
+      required List<Map<DartType, NonPromotionReason> Function()>
+          whyNotPromotedInfo}) {
     if (setNameTypeToDynamic) {
       node.methodName.staticType = _dynamicType;
     }
     node.staticInvokeType = _dynamicType;
     node.staticType = _dynamicType;
     _setExplicitTypeArgumentTypes();
-    _resolveArguments_finishInference(node);
+    _resolveArguments_finishInference(node, whyNotPromotedInfo);
   }
 
   /// Set explicitly specified type argument types, or empty if not specified.
@@ -796,26 +851,29 @@
     }
   }
 
-  void _setResolution(MethodInvocationImpl node, DartType type) {
+  void _setResolution(MethodInvocationImpl node, DartType type,
+      List<Map<DartType, NonPromotionReason> Function()> whyNotPromotedInfo) {
     // 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;
 
     if (type == _dynamicType || _isCoreFunction(type)) {
-      _setDynamicResolution(node, setNameTypeToDynamic: false);
+      _setDynamicResolution(node,
+          setNameTypeToDynamic: false, whyNotPromotedInfo: whyNotPromotedInfo);
       return;
     }
 
     if (type is FunctionType) {
-      _inferenceHelper.resolveMethodInvocation(node: node, rawType: type);
+      _inferenceHelper.resolveMethodInvocation(
+          node: node, rawType: type, whyNotPromotedInfo: whyNotPromotedInfo);
       return;
     }
 
     if (type is VoidType) {
-      return _reportUseOfVoidType(node, node.methodName);
+      return _reportUseOfVoidType(node, node.methodName, whyNotPromotedInfo);
     }
 
-    _reportInvocationOfNonFunction(node);
+    _reportInvocationOfNonFunction(node, whyNotPromotedInfo);
   }
 
   /// Resolver visitor is separated from the elements resolver, which calls
diff --git a/pkg/analyzer/lib/src/generated/element_resolver.dart b/pkg/analyzer/lib/src/generated/element_resolver.dart
index 921c948..423b5df 100644
--- a/pkg/analyzer/lib/src/generated/element_resolver.dart
+++ b/pkg/analyzer/lib/src/generated/element_resolver.dart
@@ -2,6 +2,7 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE 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';
@@ -394,8 +395,12 @@
   }
 
   @override
-  void visitMethodInvocation(MethodInvocation node) {
-    _methodInvocationResolver.resolve(node as MethodInvocationImpl);
+  void visitMethodInvocation(MethodInvocation node,
+      {List<Map<DartType, NonPromotionReason> Function()>?
+          whyNotPromotedInfo}) {
+    whyNotPromotedInfo ??= [];
+    _methodInvocationResolver.resolve(
+        node as MethodInvocationImpl, whyNotPromotedInfo);
   }
 
   @override
diff --git a/pkg/analyzer/lib/src/generated/error_detection_helpers.dart b/pkg/analyzer/lib/src/generated/error_detection_helpers.dart
index 92b0652..7ffc94c 100644
--- a/pkg/analyzer/lib/src/generated/error_detection_helpers.dart
+++ b/pkg/analyzer/lib/src/generated/error_detection_helpers.dart
@@ -2,9 +2,12 @@
 // 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/syntactic_entity.dart';
 import 'package:analyzer/dart/element/element.dart';
 import 'package:analyzer/dart/element/type.dart';
+import 'package:analyzer/diagnostic/diagnostic.dart';
 import 'package:analyzer/error/error.dart';
 import 'package:analyzer/error/listener.dart';
 import 'package:analyzer/src/dart/ast/extensions.dart';
@@ -28,7 +31,8 @@
       Expression expression,
       DartType? expectedStaticType,
       DartType actualStaticType,
-      ErrorCode errorCode) {
+      ErrorCode errorCode,
+      {Map<DartType, NonPromotionReason> Function()? whyNotPromotedInfo}) {
     // Warning case: test static type information
     if (expectedStaticType != null) {
       if (!expectedStaticType.isVoid && checkForUseOfVoidResult(expression)) {
@@ -36,7 +40,8 @@
       }
 
       checkForAssignableExpressionAtType(
-          expression, actualStaticType, expectedStaticType, errorCode);
+          expression, actualStaticType, expectedStaticType, errorCode,
+          whyNotPromotedInfo: whyNotPromotedInfo);
     }
   }
 
@@ -48,11 +53,13 @@
   ///
   /// See [StaticWarningCode.ARGUMENT_TYPE_NOT_ASSIGNABLE].
   void checkForArgumentTypeNotAssignableForArgument(Expression argument,
-      {bool promoteParameterToNullable = false}) {
+      {bool promoteParameterToNullable = false,
+      Map<DartType, NonPromotionReason> Function()? whyNotPromotedInfo}) {
     checkForArgumentTypeNotAssignableForArgument2(
       argument: argument,
       parameter: argument.staticParameterElement,
       promoteParameterToNullable: promoteParameterToNullable,
+      whyNotPromotedInfo: whyNotPromotedInfo,
     );
   }
 
@@ -60,21 +67,26 @@
     required Expression argument,
     required ParameterElement? parameter,
     required bool promoteParameterToNullable,
+    Map<DartType, NonPromotionReason> Function()? whyNotPromotedInfo,
   }) {
     var staticParameterType = parameter?.type;
     if (promoteParameterToNullable && staticParameterType != null) {
       staticParameterType =
           typeSystem.makeNullable(staticParameterType as TypeImpl);
     }
-    _checkForArgumentTypeNotAssignableWithExpectedTypes(argument,
-        staticParameterType, CompileTimeErrorCode.ARGUMENT_TYPE_NOT_ASSIGNABLE);
+    _checkForArgumentTypeNotAssignableWithExpectedTypes(
+        argument,
+        staticParameterType,
+        CompileTimeErrorCode.ARGUMENT_TYPE_NOT_ASSIGNABLE,
+        whyNotPromotedInfo);
   }
 
   bool checkForAssignableExpressionAtType(
       Expression expression,
       DartType actualStaticType,
       DartType expectedStaticType,
-      ErrorCode errorCode) {
+      ErrorCode errorCode,
+      {Map<DartType, NonPromotionReason> Function()? whyNotPromotedInfo}) {
     if (!typeSystem.isAssignableTo(actualStaticType, expectedStaticType)) {
       AstNode getErrorNode(AstNode node) {
         if (node is CascadeExpression) {
@@ -90,6 +102,8 @@
         errorCode,
         getErrorNode(expression),
         [actualStaticType, expectedStaticType],
+        computeWhyNotPromotedMessages(
+            expression, expression, whyNotPromotedInfo?.call()),
       );
       return false;
     }
@@ -118,6 +132,22 @@
     return true;
   }
 
+  /// Computes the appropriate set of context messages to report along with an
+  /// error that may have occurred because [expression] was not type promoted.
+  ///
+  /// If [expression] is `null`, it means the expression that was not type
+  /// promoted was an implicit `this`.
+  ///
+  /// [errorEntity] is the entity whose location will be associated with the
+  /// error.  This is needed for test instrumentation.
+  ///
+  /// [whyNotPromoted] should be the non-promotion details returned by the flow
+  /// analysis engine.
+  List<DiagnosticMessage> computeWhyNotPromotedMessages(
+      Expression? expression,
+      SyntacticEntity errorEntity,
+      Map<DartType, NonPromotionReason>? whyNotPromoted);
+
   /// Verify that the given [expression] can be assigned to its corresponding
   /// parameters.
   ///
@@ -131,8 +161,10 @@
   void _checkForArgumentTypeNotAssignableWithExpectedTypes(
       Expression expression,
       DartType? expectedStaticType,
-      ErrorCode errorCode) {
+      ErrorCode errorCode,
+      Map<DartType, NonPromotionReason> Function()? whyNotPromotedInfo) {
     checkForArgumentTypeNotAssignable(
-        expression, expectedStaticType, expression.typeOrThrow, errorCode);
+        expression, expectedStaticType, expression.typeOrThrow, errorCode,
+        whyNotPromotedInfo: whyNotPromotedInfo);
   }
 }
diff --git a/pkg/analyzer/lib/src/generated/error_verifier.dart b/pkg/analyzer/lib/src/generated/error_verifier.dart
index 4dd8078..55a6019 100644
--- a/pkg/analyzer/lib/src/generated/error_verifier.dart
+++ b/pkg/analyzer/lib/src/generated/error_verifier.dart
@@ -4,6 +4,7 @@
 
 import 'dart:collection';
 
+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';
@@ -13,6 +14,7 @@
 import 'package:analyzer/dart/element/nullability_suffix.dart';
 import 'package:analyzer/dart/element/type.dart';
 import 'package:analyzer/dart/element/type_provider.dart';
+import 'package:analyzer/diagnostic/diagnostic.dart';
 import 'package:analyzer/error/error.dart';
 import 'package:analyzer/error/listener.dart';
 import 'package:analyzer/src/dart/analysis/session.dart';
@@ -309,6 +311,14 @@
       _featureSet?.isEnabled(Feature.non_nullable) ?? false;
 
   @override
+  List<DiagnosticMessage> computeWhyNotPromotedMessages(
+      Expression? expression,
+      SyntacticEntity errorEntity,
+      Map<DartType, NonPromotionReason>? whyNotPromoted) {
+    return [];
+  }
+
+  @override
   void visitAnnotation(Annotation node) {
     _checkForInvalidAnnotationFromDeferredLibrary(node);
     _checkForMissingJSLibAnnotation(node);
diff --git a/pkg/analyzer/lib/src/generated/resolver.dart b/pkg/analyzer/lib/src/generated/resolver.dart
index 7027e52..dff2a86 100644
--- a/pkg/analyzer/lib/src/generated/resolver.dart
+++ b/pkg/analyzer/lib/src/generated/resolver.dart
@@ -427,9 +427,13 @@
   /// [BestPracticesVerifier.checkForArgumentTypesNotAssignableInList].
   ///
   /// See [StaticWarningCode.ARGUMENT_TYPE_NOT_ASSIGNABLE].
-  void checkForArgumentTypesNotAssignableInList(ArgumentList argumentList) {
-    for (Expression argument in argumentList.arguments) {
-      checkForArgumentTypeNotAssignableForArgument(argument);
+  void checkForArgumentTypesNotAssignableInList(ArgumentList argumentList,
+      List<Map<DartType, NonPromotionReason> Function()> whyNotPromotedInfo) {
+    var arguments = argumentList.arguments;
+    for (int i = 0; i < arguments.length; i++) {
+      checkForArgumentTypeNotAssignableForArgument(arguments[i],
+          whyNotPromotedInfo:
+              flowAnalysis?.flow == null ? null : whyNotPromotedInfo[i]);
     }
   }
 
@@ -527,21 +531,14 @@
     nullSafetyDeadCodeVerifier.visitNode(node);
   }
 
-  /// Computes the appropriate set of context messages to report along with an
-  /// error that may have occurred because [expression] was not type promoted.
-  ///
-  /// If [expression] is `null`, it means the expression that was not type
-  /// promoted was an implicit `this`.
-  ///
-  /// [errorEntity] is the entity whose location will be associated with the
-  /// error.  This is needed for test instrumentation.
-  ///
-  /// [whyNotPromoted] should be the non-promotion details returned by the flow
-  /// analysis engine.
+  @override
   List<DiagnosticMessage> computeWhyNotPromotedMessages(
       Expression? expression,
       SyntacticEntity errorEntity,
       Map<DartType, NonPromotionReason>? whyNotPromoted) {
+    if (expression is NamedExpression) {
+      expression = expression.expression;
+    }
     List<DiagnosticMessage> messages = [];
     if (whyNotPromoted != null) {
       for (var entry in whyNotPromoted.entries) {
@@ -885,21 +882,25 @@
 
   @override
   void visitAnnotation(covariant AnnotationImpl node) {
+    var whyNotPromotedInfo = <Map<DartType, NonPromotionReason> Function()>[];
     AstNode parent = node.parent;
     if (identical(parent, _enclosingClassDeclaration) ||
         identical(parent, _enclosingFunctionTypeAlias) ||
         identical(parent, _enclosingMixinDeclaration)) {
       return;
     }
-    AnnotationResolver(this).resolve(node);
+    AnnotationResolver(this).resolve(node, whyNotPromotedInfo);
     var arguments = node.arguments;
     if (arguments != null) {
-      checkForArgumentTypesNotAssignableInList(arguments);
+      checkForArgumentTypesNotAssignableInList(arguments, whyNotPromotedInfo);
     }
   }
 
   @override
-  void visitArgumentList(ArgumentList node, {bool isIdentical = false}) {
+  void visitArgumentList(ArgumentList node,
+      {bool isIdentical = false,
+      List<Map<DartType, NonPromotionReason> Function()>? whyNotPromotedInfo}) {
+    whyNotPromotedInfo ??= [];
     var callerType = InferenceContext.getContext(node);
     NodeList<Expression> arguments = node.arguments;
     if (callerType is FunctionType) {
@@ -958,17 +959,20 @@
     }
     checkUnreachableNode(node);
     int length = arguments.length;
+    var flow = flowAnalysis?.flow;
     for (var i = 0; i < length; i++) {
       if (isIdentical && length > 1 && i == 1) {
         var firstArg = arguments[0];
-        flowAnalysis?.flow
-            ?.equalityOp_rightBegin(firstArg, firstArg.typeOrThrow);
+        flow?.equalityOp_rightBegin(firstArg, firstArg.typeOrThrow);
       }
       arguments[i].accept(this);
+      if (flow != null) {
+        whyNotPromotedInfo.add(flow.whyNotPromoted(arguments[i]));
+      }
     }
     if (isIdentical && length > 1) {
       var secondArg = arguments[1];
-      flowAnalysis?.flow?.equalityOp_end(
+      flow?.equalityOp_end(
           node.parent as Expression, secondArg, secondArg.typeOrThrow);
     }
     node.accept(elementResolver);
@@ -1427,11 +1431,13 @@
 
   @override
   void visitExtensionOverride(ExtensionOverride node) {
+    var whyNotPromotedInfo = <Map<DartType, NonPromotionReason> Function()>[];
     node.extensionName.accept(this);
     node.typeArguments?.accept(this);
 
     ExtensionMemberResolver(this).setOverrideReceiverContextType(node);
-    node.argumentList.accept(this);
+    visitArgumentList(node.argumentList,
+        whyNotPromotedInfo: whyNotPromotedInfo);
 
     node.accept(elementResolver);
     node.accept(typeAnalyzer);
@@ -1548,11 +1554,13 @@
 
   @override
   void visitFunctionExpressionInvocation(FunctionExpressionInvocation node) {
+    var whyNotPromotedInfo = <Map<DartType, NonPromotionReason> Function()>[];
     node.function.accept(this);
-    _functionExpressionInvocationResolver
-        .resolve(node as FunctionExpressionInvocationImpl);
+    _functionExpressionInvocationResolver.resolve(
+        node as FunctionExpressionInvocationImpl, whyNotPromotedInfo);
     nullShortingTermination(node);
-    checkForArgumentTypesNotAssignableInList(node.argumentList);
+    checkForArgumentTypesNotAssignableInList(
+        node.argumentList, whyNotPromotedInfo);
   }
 
   @override
@@ -1694,12 +1702,15 @@
   @override
   void visitInstanceCreationExpression(
       covariant InstanceCreationExpressionImpl node) {
+    var whyNotPromotedInfo = <Map<DartType, NonPromotionReason> Function()>[];
     node.constructorName.accept(this);
     _inferArgumentTypesForInstanceCreate(node);
-    node.argumentList.accept(this);
+    visitArgumentList(node.argumentList,
+        whyNotPromotedInfo: whyNotPromotedInfo);
     node.accept(elementResolver);
     node.accept(typeAnalyzer);
-    checkForArgumentTypesNotAssignableInList(node.argumentList);
+    checkForArgumentTypesNotAssignableInList(
+        node.argumentList, whyNotPromotedInfo);
   }
 
   @override
@@ -1764,6 +1775,7 @@
 
   @override
   void visitMethodInvocation(covariant MethodInvocationImpl node) {
+    var whyNotPromotedInfo = <Map<DartType, NonPromotionReason> Function()>[];
     var target = node.target;
     target?.accept(this);
 
@@ -1782,16 +1794,19 @@
     }
 
     node.typeArguments?.accept(this);
-    node.accept(elementResolver);
+    elementResolver.visitMethodInvocation(node,
+        whyNotPromotedInfo: whyNotPromotedInfo);
 
     var functionRewrite = MethodInvocationResolver.getRewriteResult(node);
     if (functionRewrite != null) {
       nullShortingTermination(node, discardType: true);
-      _resolveRewrittenFunctionExpressionInvocation(functionRewrite);
+      _resolveRewrittenFunctionExpressionInvocation(
+          functionRewrite, whyNotPromotedInfo);
     } else {
       nullShortingTermination(node);
     }
-    checkForArgumentTypesNotAssignableInList(node.argumentList);
+    checkForArgumentTypesNotAssignableInList(
+        node.argumentList, whyNotPromotedInfo);
   }
 
   @override
@@ -1904,11 +1919,14 @@
     // because it needs to be visited in the context of the constructor
     // invocation.
     //
+    var whyNotPromotedInfo = <Map<DartType, NonPromotionReason> Function()>[];
     node.accept(elementResolver);
     InferenceContext.setType(node.argumentList, node.staticElement?.type);
-    node.argumentList.accept(this);
+    visitArgumentList(node.argumentList,
+        whyNotPromotedInfo: whyNotPromotedInfo);
     node.accept(typeAnalyzer);
-    checkForArgumentTypesNotAssignableInList(node.argumentList);
+    checkForArgumentTypesNotAssignableInList(
+        node.argumentList, whyNotPromotedInfo);
   }
 
   @override
@@ -1962,11 +1980,14 @@
     // because it needs to be visited in the context of the constructor
     // invocation.
     //
+    var whyNotPromotedInfo = <Map<DartType, NonPromotionReason> Function()>[];
     node.accept(elementResolver);
     InferenceContext.setType(node.argumentList, node.staticElement?.type);
-    node.argumentList.accept(this);
+    visitArgumentList(node.argumentList,
+        whyNotPromotedInfo: whyNotPromotedInfo);
     node.accept(typeAnalyzer);
-    checkForArgumentTypesNotAssignableInList(node.argumentList);
+    checkForArgumentTypesNotAssignableInList(
+        node.argumentList, whyNotPromotedInfo);
   }
 
   @override
@@ -2271,6 +2292,7 @@
   /// as for method invocations.
   void _resolveRewrittenFunctionExpressionInvocation(
     FunctionExpressionInvocation node,
+    List<Map<DartType, NonPromotionReason> Function()> whyNotPromotedInfo,
   ) {
     var function = node.function;
 
@@ -2287,8 +2309,8 @@
       }
     }
 
-    _functionExpressionInvocationResolver
-        .resolve(node as FunctionExpressionInvocationImpl);
+    _functionExpressionInvocationResolver.resolve(
+        node as FunctionExpressionInvocationImpl, whyNotPromotedInfo);
 
     nullShortingTermination(node);
   }
diff --git a/pkg/analyzer/lib/src/pubspec/pubspec_validator.dart b/pkg/analyzer/lib/src/pubspec/pubspec_validator.dart
index c7846a4..077b56b 100644
--- a/pkg/analyzer/lib/src/pubspec/pubspec_validator.dart
+++ b/pkg/analyzer/lib/src/pubspec/pubspec_validator.dart
@@ -5,7 +5,6 @@
 import 'package:analyzer/error/error.dart';
 import 'package:analyzer/error/listener.dart';
 import 'package:analyzer/file_system/file_system.dart';
-import 'package:analyzer/src/generated/engine.dart';
 import 'package:analyzer/src/generated/source.dart';
 import 'package:analyzer/src/pubspec/pubspec_warning_code.dart';
 import 'package:analyzer/src/util/file_paths.dart' as file_paths;
diff --git a/pkg/analyzer/lib/src/workspace/bazel_watcher.dart b/pkg/analyzer/lib/src/workspace/bazel_watcher.dart
index 5d76920..1109c63 100644
--- a/pkg/analyzer/lib/src/workspace/bazel_watcher.dart
+++ b/pkg/analyzer/lib/src/workspace/bazel_watcher.dart
@@ -435,9 +435,13 @@
   /// Determines how often do we check for `command.log` changes.
   static const _pollInterval = Duration(seconds: 1);
 
-  /// To confirm that a build finished, we check for this message in the
+  /// To confirm that a build finished, we check for these messages in the
   /// `command.log`.
-  static const _buildCompletedMsg = 'Build completed successfully';
+  static const _buildCompletedMsgs = [
+    'Build completed successfully',
+    'Build completed',
+    'Build did NOT complete successfully',
+  ];
 
   final _controller = StreamController<WatchEvent>.broadcast();
   final ResourceProvider _provider;
@@ -459,7 +463,7 @@
   bool _buildFinished(String contents) {
     // Only look at the last 100 characters.
     var offset = max(0, contents.length - 100);
-    return contents.contains(_buildCompletedMsg, offset);
+    return _buildCompletedMsgs.any((msg) => contents.contains(msg, offset));
   }
 
   Future<String?> _getCommandLogPath() async {
diff --git a/pkg/analyzer/tool/summary/stats.dart b/pkg/analyzer/tool/summary/stats.dart
index 86c0677..d03b7db 100644
--- a/pkg/analyzer/tool/summary/stats.dart
+++ b/pkg/analyzer/tool/summary/stats.dart
@@ -66,6 +66,9 @@
           value == false ||
           value == '' ||
           value is List && value.isEmpty ||
+          // TODO(srawlins): Remove this and enumerate each enum which may
+          // be encountered.
+          // ignore: avoid_dynamic_calls
           reflect(value).type.isEnum && (value as dynamic).index == 0) {
         return;
       }
diff --git a/pkg/compiler/lib/src/js_backend/namer_names.dart b/pkg/compiler/lib/src/js_backend/namer_names.dart
index ce57087..233f053 100644
--- a/pkg/compiler/lib/src/js_backend/namer_names.dart
+++ b/pkg/compiler/lib/src/js_backend/namer_names.dart
@@ -146,6 +146,9 @@
   CompoundName.from(List<jsAst.Name> parts) : this(<_NamerName>[...parts]);
 
   @override
+  bool get isFinalized => _parts.every((name) => name.isFinalized);
+
+  @override
   String get name {
     if (_cachedName == null) {
       _cachedName = _parts.map((jsAst.Name name) => name.name).join();
diff --git a/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_merger.dart b/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_merger.dart
index d9d65cc..122a716 100644
--- a/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_merger.dart
+++ b/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_merger.dart
@@ -2,6 +2,7 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
+import 'dart:collection';
 import '../../deferred_load.dart' show OutputUnit;
 import '../../js/js.dart' as js;
 import '../../js/size_estimator.dart';
@@ -283,6 +284,7 @@
 class _Partition {
   int size = 0;
   List<PreFragment> fragments = [];
+  bool isClosed = false;
 
   void add(PreFragment that) {
     size += that.size;
@@ -377,6 +379,38 @@
     });
   }
 
+  /// Given a list of [PreFragments], returns a list of lists of [PreFragments]
+  /// where each list represents a component in the graph.
+  List<List<PreFragment>> separateComponents(
+      List<PreFragment> preDeferredFragments) {
+    List<List<PreFragment>> components = [];
+    Set<PreFragment> visited = {};
+
+    // Starting from each 'root' in the graph, use bfs to find a component.
+    for (var preFragment in preDeferredFragments) {
+      if (preFragment.predecessors.isEmpty && visited.add(preFragment)) {
+        List<PreFragment> component = [];
+        var queue = Queue<PreFragment>();
+        queue.add(preFragment);
+        while (queue.isNotEmpty) {
+          var preFragment = queue.removeFirst();
+          component.add(preFragment);
+          preFragment.predecessors.where(visited.add).forEach(queue.add);
+          preFragment.successors.where(visited.add).forEach(queue.add);
+        }
+
+        // Sort the fragments in the component so they will be in a canonical
+        // order.
+        component.sort((a, b) {
+          return a.fragments.single.outputUnit
+              .compareTo(b.fragments.single.outputUnit);
+        });
+        components.add(component);
+      }
+    }
+    return components;
+  }
+
   /// A trivial greedy merge that uses the sorted order of the output units to
   /// merge contiguous runs of fragments without creating cycles.
   /// ie, if our sorted output units look like:
@@ -386,28 +420,26 @@
   /// fragment size of 5. Our final partitions would look like:
   ///   {a}, {b}, {c}+{a, b}, {b, c}+{a, b, c}.
   List<PreFragment> mergeFragments(List<PreFragment> preDeferredFragments) {
-    // Sort PreFragments by their initial OutputUnit so they are in canonical
-    // order.
-    preDeferredFragments.sort((a, b) {
-      return a.fragments.single.outputUnit
-          .compareTo(b.fragments.single.outputUnit);
-    });
+    var components = separateComponents(preDeferredFragments);
     int desiredNumberOfFragment = _options.mergeFragmentsThreshold;
-
     int idealFragmentSize = (totalSize / desiredNumberOfFragment).ceil();
     List<_Partition> partitions = [];
     void add(PreFragment next) {
       // Create a new partition if the current one grows too large, otherwise
       // just add to the most recent partition.
       if (partitions.isEmpty ||
+          partitions.last.isClosed ||
           partitions.last.size + next.size > idealFragmentSize) {
         partitions.add(_Partition());
       }
       partitions.last.add(next);
     }
 
-    // Greedily group fragments into partitions.
-    preDeferredFragments.forEach(add);
+    // Greedily group fragments into partitions, but only within each component.
+    for (var component in components) {
+      component.forEach(add);
+      partitions.last.isClosed = true;
+    }
 
     // Reduce fragments by merging fragments with fewer imports into fragments
     // with more imports.
diff --git a/pkg/compiler/test/deferred_loading/data/components/libA.dart b/pkg/compiler/test/deferred_loading/data/components/libA.dart
new file mode 100644
index 0000000..156d3fc
--- /dev/null
+++ b/pkg/compiler/test/deferred_loading/data/components/libA.dart
@@ -0,0 +1,8 @@
+// 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.
+
+/*member: component:member_unit=1{libA}*/
+String component() {
+  return 'libA';
+}
diff --git a/pkg/compiler/test/deferred_loading/data/components/libB.dart b/pkg/compiler/test/deferred_loading/data/components/libB.dart
new file mode 100644
index 0000000..87b10d5
--- /dev/null
+++ b/pkg/compiler/test/deferred_loading/data/components/libB.dart
@@ -0,0 +1,10 @@
+// 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.
+
+import "libBCDE.dart";
+
+/*member: component:member_unit=2{libB}*/
+String component() {
+  return componentBCDE();
+}
diff --git a/pkg/compiler/test/deferred_loading/data/components/libBCDE.dart b/pkg/compiler/test/deferred_loading/data/components/libBCDE.dart
new file mode 100644
index 0000000..77a74fe
--- /dev/null
+++ b/pkg/compiler/test/deferred_loading/data/components/libBCDE.dart
@@ -0,0 +1,9 @@
+// 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.
+
+@pragma('dart2js:noInline')
+/*member: componentBCDE:member_unit=3{libB, libC, libD, libE}*/
+String componentBCDE() {
+  return 'libBCDE';
+}
diff --git a/pkg/compiler/test/deferred_loading/data/components/libC.dart b/pkg/compiler/test/deferred_loading/data/components/libC.dart
new file mode 100644
index 0000000..d13a2f5
--- /dev/null
+++ b/pkg/compiler/test/deferred_loading/data/components/libC.dart
@@ -0,0 +1,10 @@
+// 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.
+
+import "libBCDE.dart";
+
+/*member: component:member_unit=4{libC}*/
+String component() {
+  return componentBCDE();
+}
diff --git a/pkg/compiler/test/deferred_loading/data/components/libD.dart b/pkg/compiler/test/deferred_loading/data/components/libD.dart
new file mode 100644
index 0000000..c20123e
--- /dev/null
+++ b/pkg/compiler/test/deferred_loading/data/components/libD.dart
@@ -0,0 +1,10 @@
+// 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.
+
+import "libBCDE.dart";
+
+/*member: component:member_unit=5{libD}*/
+String component() {
+  return componentBCDE();
+}
diff --git a/pkg/compiler/test/deferred_loading/data/components/libE.dart b/pkg/compiler/test/deferred_loading/data/components/libE.dart
new file mode 100644
index 0000000..fd8e3c2
--- /dev/null
+++ b/pkg/compiler/test/deferred_loading/data/components/libE.dart
@@ -0,0 +1,10 @@
+// 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.
+
+import "libBCDE.dart";
+
+/*member: component:member_unit=6{libE}*/
+String component() {
+  return componentBCDE();
+}
diff --git a/pkg/compiler/test/deferred_loading/data/components/main.dart b/pkg/compiler/test/deferred_loading/data/components/main.dart
new file mode 100644
index 0000000..e918bf6
--- /dev/null
+++ b/pkg/compiler/test/deferred_loading/data/components/main.dart
@@ -0,0 +1,69 @@
+// 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.
+
+/*spec.library: 
+ output_units=[
+  f1: {units: [1{libA}], usedBy: [], needs: []},
+  f2: {units: [3{libB, libC, libD, libE}], usedBy: [], needs: []},
+  f3: {units: [2{libB}], usedBy: [], needs: []},
+  f4: {units: [4{libC}], usedBy: [], needs: []},
+  f5: {units: [5{libD}], usedBy: [], needs: []},
+  f6: {units: [6{libE}], usedBy: [], needs: []}],
+ steps=[
+  libA=(f1),
+  libB=(f2, f3),
+  libC=(f2, f4),
+  libD=(f2, f5),
+  libE=(f2, f6)]
+*/
+
+/*two-frag.library: 
+ output_units=[
+  f1: {units: [1{libA}], usedBy: [], needs: []},
+  f2: {units: [3{libB, libC, libD, libE}, 6{libE}], usedBy: [], needs: [3]},
+  f3: {units: [5{libD}, 4{libC}, 2{libB}], usedBy: [2], needs: []}],
+ steps=[
+  libA=(f1),
+  libB=(f2, f3),
+  libC=(f2, f3),
+  libD=(f2, f3),
+  libE=(f2)]
+*/
+
+/*three-frag.library: 
+ output_units=[
+  f1: {units: [1{libA}], usedBy: [], needs: []},
+  f2: {units: [3{libB, libC, libD, libE}], usedBy: [], needs: [3, 4]},
+  f3: {units: [4{libC}, 2{libB}], usedBy: [2], needs: []},
+  f4: {units: [6{libE}, 5{libD}], usedBy: [2], needs: []}],
+ steps=[
+  libA=(f1),
+  libB=(f2, f3),
+  libC=(f2, f3),
+  libD=(f2, f4),
+  libE=(f2, f4)]
+*/
+
+// @dart = 2.7
+
+import 'libA.dart' deferred as libA;
+import 'libB.dart' deferred as libB;
+import 'libC.dart' deferred as libC;
+import 'libD.dart' deferred as libD;
+import 'libE.dart' deferred as libE;
+
+/*member: main:member_unit=main{}*/
+main() async {
+  await libA.loadLibrary();
+  await libB.loadLibrary();
+  await libC.loadLibrary();
+  await libD.loadLibrary();
+  await libE.loadLibrary();
+
+  print(libA.component());
+  print(libB.component());
+  print(libC.component());
+  print(libD.component());
+  print(libE.component());
+}
diff --git a/pkg/dart_internal/lib/extract_type_arguments.dart b/pkg/dart_internal/lib/extract_type_arguments.dart
index 4275e70..6a02827 100644
--- a/pkg/dart_internal/lib/extract_type_arguments.dart
+++ b/pkg/dart_internal/lib/extract_type_arguments.dart
@@ -9,7 +9,6 @@
 //
 // Only this exact special file is allowed to import "dart:_internal" without
 // causing a compile error.
-// ignore: import_internal_library
 import 'dart:_internal' as internal;
 
 /// Given an [Iterable], invokes [extract], passing the [iterable]'s type
diff --git a/runtime/platform/BUILD.gn b/runtime/platform/BUILD.gn
index c727eea..f339a1e 100644
--- a/runtime/platform/BUILD.gn
+++ b/runtime/platform/BUILD.gn
@@ -18,11 +18,20 @@
 
   if (is_fuchsia) {
     if (using_fuchsia_gn_sdk) {
-      extra_deps += [ "$fuchsia_sdk_root/pkg/sys_cpp" ]
+      extra_deps += [
+        "$fuchsia_sdk_root/pkg/sys_cpp",
+        "$fuchsia_sdk_root/pkg/sys_inspect_cpp",
+      ]
     } else if (using_fuchsia_sdk) {
-      extra_deps += [ "$fuchsia_sdk_root/pkg:sys_cpp" ]
+      extra_deps += [
+        "$fuchsia_sdk_root/pkg:sys_cpp",
+        "$fuchsia_sdk_root/pkg:sys_inspect_cpp",
+      ]
     } else {
-      extra_deps += [ "//sdk/lib/sys/cpp" ]
+      extra_deps += [
+        "//sdk/lib/sys/cpp",
+        "//sdk/lib/sys/inspect/cpp",
+      ]
     }
   }
 }
diff --git a/runtime/platform/utils_fuchsia.cc b/runtime/platform/utils_fuchsia.cc
index cfa17e6..a6d9f37 100644
--- a/runtime/platform/utils_fuchsia.cc
+++ b/runtime/platform/utils_fuchsia.cc
@@ -6,8 +6,10 @@
 #if defined(HOST_OS_FUCHSIA)
 
 #include <memory>
+#include <utility>
 
 #include "lib/sys/cpp/component_context.h"
+#include "lib/sys/inspect/cpp/component.h"
 #include "platform/utils.h"
 #include "platform/utils_fuchsia.h"
 
@@ -57,6 +59,23 @@
   return context.get();
 }
 
+std::unique_ptr<inspect::Node> vm_node;
+void SetDartVmNode(std::unique_ptr<inspect::Node> node) {
+  vm_node = std::move(node);
+}
+
+std::unique_ptr<inspect::Node> TakeDartVmNode() {
+  // TODO(fxbug.dev/69558) Remove the creation of the node_ from this call
+  // after the runners have been migrated to injecting this object.
+  if (vm_node == nullptr) {
+    static std::unique_ptr<sys::ComponentInspector> component_inspector =
+        std::make_unique<sys::ComponentInspector>(dart::ComponentContext());
+    inspect::Node& root = component_inspector->inspector()->GetRoot();
+    vm_node = std::make_unique<inspect::Node>(root.CreateChild("vm"));
+  }
+  return std::move(vm_node);
+}
+
 }  // namespace dart
 
 #endif  // defined(HOST_OS_FUCHSIA)
diff --git a/runtime/platform/utils_fuchsia.h b/runtime/platform/utils_fuchsia.h
index 4b2064e..b88328a 100644
--- a/runtime/platform/utils_fuchsia.h
+++ b/runtime/platform/utils_fuchsia.h
@@ -6,6 +6,7 @@
 #define RUNTIME_PLATFORM_UTILS_FUCHSIA_H_
 
 #include <endian.h>
+#include <memory>
 
 namespace sys {
 
@@ -14,6 +15,13 @@
 
 }  // namespace sys
 
+namespace inspect {
+
+// From Fuchsia SDK.
+class Node;
+
+}  // namespace inspect
+
 namespace dart {
 
 inline uint16_t Utils::HostToBigEndian16(uint16_t value) {
@@ -57,6 +65,18 @@
 // call sys::ComponentContext::Create().
 sys::ComponentContext* ComponentContext();
 
+// Sets the inspect node set to be used in the dart vm
+//
+// This method will take ownership of the node
+void SetDartVmNode(std::unique_ptr<inspect::Node> node);
+
+// Returns the inspect node set in SetDartVmNode().
+//
+// The caller should take ownership of the returned node because
+// the value will be set to null after this call.
+// This call may return null if no node is provided.
+std::unique_ptr<inspect::Node> TakeDartVmNode();
+
 }  // namespace dart
 
 #endif  // RUNTIME_PLATFORM_UTILS_FUCHSIA_H_
diff --git a/runtime/vm/os_fuchsia.cc b/runtime/vm/os_fuchsia.cc
index 674bdd5..46c4303 100644
--- a/runtime/vm/os_fuchsia.cc
+++ b/runtime/vm/os_fuchsia.cc
@@ -80,23 +80,21 @@
 // Under normal operation, all metric values below should be zero.
 class InspectMetrics {
  public:
-  // Does not take ownership of inspector.
-  explicit InspectMetrics(inspect::Inspector* inspector)
-      : inspector_(inspector),
-        root_(inspector_->GetRoot()),
-        metrics_(root_.CreateChild("os")),
-        dst_status_(metrics_.CreateInt("dst_status", kUninitialized)),
-        tz_data_status_(metrics_.CreateInt("tz_data_status", kUninitialized)),
+  // Takes ownership of the vm_node.
+  explicit InspectMetrics(std::unique_ptr<inspect::Node> vm_node)
+      : vm_node_(std::move(vm_node)),
+        dst_status_(vm_node_->CreateInt("dst_status", kUninitialized)),
+        tz_data_status_(vm_node_->CreateInt("tz_data_status", kUninitialized)),
         tz_data_close_status_(
-            metrics_.CreateInt("tz_data_close_status", kUninitialized)),
+            vm_node_->CreateInt("tz_data_close_status", kUninitialized)),
         get_profile_status_(
-            metrics_.CreateInt("get_profile_status", kUninitialized)),
+            vm_node_->CreateInt("get_profile_status", kUninitialized)),
         profiles_timezone_content_status_(
-            metrics_.CreateInt("timezone_content_status", kOk)),
-        num_get_profile_calls_(metrics_.CreateInt("num_get_profile_calls", 0)),
-        num_on_change_calls_(metrics_.CreateInt("num_on_change_calls", 0)),
+            vm_node_->CreateInt("timezone_content_status", kOk)),
+        num_get_profile_calls_(vm_node_->CreateInt("num_get_profile_calls", 0)),
+        num_on_change_calls_(vm_node_->CreateInt("num_on_change_calls", 0)),
         num_intl_provider_errors_(
-            metrics_.CreateInt("num_intl_provider_errors", 0)) {}
+            vm_node_->CreateInt("num_intl_provider_errors", 0)) {}
 
   // Registers a single call to GetProfile callback.
   void RegisterGetProfileCall() { num_get_profile_calls_.Add(1); }
@@ -131,14 +129,8 @@
   }
 
  private:
-  // The inspector that all metrics are being reported into.
-  inspect::Inspector* inspector_;
-
-  // References inspector_ state.
-  inspect::Node& root_;
-
   // The OS metrics node.
-  inspect::Node metrics_;
+  std::unique_ptr<inspect::Node> vm_node_;
 
   // The status of the last GetTimeZoneOffset call.
   inspect::IntProperty dst_status_;
@@ -305,7 +297,6 @@
 std::set<const std::string> timezone_names;
 
 // Initialized on OS:Init(), deinitialized on OS::Cleanup.
-std::unique_ptr<sys::ComponentInspector> component_inspector;
 std::shared_ptr<InspectMetrics> metrics;
 std::shared_ptr<TimezoneName> timezone_name;
 async_loop_t* message_loop = nullptr;
@@ -603,9 +594,11 @@
     async_loop_start_thread(message_loop, "Fuchsia async loop", nullptr);
   }
 
-  sys::ComponentContext* context = dart::ComponentContext();
-  component_inspector = std::make_unique<sys::ComponentInspector>(context);
-  metrics = std::make_shared<InspectMetrics>(component_inspector->inspector());
+  auto vm_node = dart::TakeDartVmNode();
+
+  // TODO(fxbug.dev/69558) allow vm_node to be null and not crash
+  ASSERT(vm_node != nullptr);
+  metrics = std::make_shared<InspectMetrics>(std::move(vm_node));
 
   InitializeTZData();
   auto services = sys::ServiceDirectory::CreateFromNamespace();
@@ -620,7 +613,6 @@
   }
   timezone_name.reset();
   metrics.reset();
-  component_inspector.reset();
 
   if (message_loop != nullptr) {
     // Check message_loop is still the default dispatcher before clearing it.
diff --git a/runtime/vm/timeline.cc b/runtime/vm/timeline.cc
index 906a9ee..609f66e 100644
--- a/runtime/vm/timeline.cc
+++ b/runtime/vm/timeline.cc
@@ -86,8 +86,6 @@
 //
 // Locking notes:
 // The following locks are used by the timeline system:
-// - |Timeline::recorder_lock_| This lock is held whenever Timeline::recorder()
-// is accessed or modified.
 // - |TimelineEventRecorder::lock_| This lock is held whenever a
 // |TimelineEventBlock| is being requested or reclaimed.
 // - |Thread::timeline_block_lock_| This lock is held whenever a |Thread|'s
@@ -96,10 +94,9 @@
 // |Thread|s.
 //
 // Locks must always be taken in the following order:
-//   |Timeline::recorder_lock_|
-//     |Thread::thread_list_lock_|
-//       |Thread::timeline_block_lock_|
-//         |TimelineEventRecorder::lock_|
+//   |Thread::thread_list_lock_|
+//     |Thread::timeline_block_lock_|
+//       |TimelineEventRecorder::lock_|
 //
 
 static TimelineEventRecorder* CreateTimelineRecorder() {
@@ -208,10 +205,6 @@
 }
 
 void Timeline::Init() {
-  if (recorder_lock_ == nullptr) {
-    recorder_lock_ = new Mutex();
-  }
-  MutexLocker ml(recorder_lock_);
   ASSERT(recorder_ == NULL);
   recorder_ = CreateTimelineRecorder();
   ASSERT(recorder_ != NULL);
@@ -224,31 +217,25 @@
 }
 
 void Timeline::Cleanup() {
-  ASSERT(recorder_lock_ != nullptr);
-  TimelineEventRecorder* temp = recorder_;
-  {
-    MutexLocker ml(recorder_lock_);
-    ASSERT(recorder_ != NULL);
+  ASSERT(recorder_ != NULL);
 
 #ifndef PRODUCT
-    if (FLAG_timeline_dir != NULL) {
-      recorder_->WriteTo(FLAG_timeline_dir);
-    }
+  if (FLAG_timeline_dir != NULL) {
+    recorder_->WriteTo(FLAG_timeline_dir);
+  }
 #endif
 
-    // Disable global streams.
+// Disable global streams.
 #define TIMELINE_STREAM_DISABLE(name, fuchsia_name)                            \
   Timeline::stream_##name##_.set_enabled(false);
-    TIMELINE_STREAM_LIST(TIMELINE_STREAM_DISABLE)
+  TIMELINE_STREAM_LIST(TIMELINE_STREAM_DISABLE)
 #undef TIMELINE_STREAM_DISABLE
-    recorder_ = NULL;
-    if (enabled_streams_ != NULL) {
-      FreeEnabledByDefaultTimelineStreams(enabled_streams_);
-      enabled_streams_ = NULL;
-    }
+  delete recorder_;
+  recorder_ = NULL;
+  if (enabled_streams_ != NULL) {
+    FreeEnabledByDefaultTimelineStreams(enabled_streams_);
+    enabled_streams_ = NULL;
   }
-  // We need to release the recorder lock to finish cleanup.
-  delete temp;
 }
 
 TimelineEventRecorder* Timeline::recorder() {
@@ -256,29 +243,23 @@
 }
 
 void Timeline::ReclaimCachedBlocksFromThreads() {
-  ASSERT(recorder_lock_ != nullptr);
-  MutexLocker ml(recorder_lock_);
-  ReclaimCachedBlocksFromThreadsLocked();
-}
-
-void Timeline::ReclaimCachedBlocksFromThreadsLocked() {
-  ASSERT(recorder_lock_->IsOwnedByCurrentThread());
   TimelineEventRecorder* recorder = Timeline::recorder();
+  if (recorder == NULL) {
+    return;
+  }
+
   // Iterate over threads.
   OSThreadIterator it;
   while (it.HasNext()) {
     OSThread* thread = it.Next();
-    // TODO(johnmccutchan): Consider dropping the timeline_block_lock here
-    // if we can do it everywhere. This would simplify the lock ordering
-    // requirements.
-
     MutexLocker ml(thread->timeline_block_lock());
     // Grab block and clear it.
     TimelineEventBlock* block = thread->timeline_block();
     thread->set_timeline_block(NULL);
-    if (recorder != nullptr) {
-      recorder->FinishBlock(block);
-    }
+    // TODO(johnmccutchan): Consider dropping the timeline_block_lock here
+    // if we can do it everywhere. This would simplify the lock ordering
+    // requirements.
+    recorder->FinishBlock(block);
   }
 }
 
@@ -293,8 +274,6 @@
 }
 
 void Timeline::PrintFlagsToJSON(JSONStream* js) {
-  ASSERT(recorder_lock_ != nullptr);
-  MutexLocker ml(recorder_lock_);
   JSONObject obj(js);
   obj.AddProperty("type", "TimelineFlags");
   TimelineEventRecorder* recorder = Timeline::recorder();
@@ -322,13 +301,11 @@
 #endif
 
 void Timeline::Clear() {
-  ASSERT(recorder_lock_ != nullptr);
-  MutexLocker ml(recorder_lock_);
   TimelineEventRecorder* recorder = Timeline::recorder();
   if (recorder == NULL) {
     return;
   }
-  ReclaimCachedBlocksFromThreadsLocked();
+  ReclaimCachedBlocksFromThreads();
   recorder->Clear();
 }
 
@@ -413,7 +390,6 @@
 
 TimelineEventRecorder* Timeline::recorder_ = NULL;
 MallocGrowableArray<char*>* Timeline::enabled_streams_ = NULL;
-Mutex* Timeline::recorder_lock_ = nullptr;
 
 #define TIMELINE_STREAM_DEFINE(name, fuchsia_name)                             \
   TimelineStream Timeline::stream_##name##_(#name, fuchsia_name, false);
@@ -580,8 +556,6 @@
 }
 
 void TimelineEvent::Complete() {
-  ASSERT(Timeline::recorder_lock() != nullptr);
-  MutexLocker ml(Timeline::recorder_lock());
   TimelineEventRecorder* recorder = Timeline::recorder();
   if (recorder != NULL) {
     recorder->CompleteEvent(this);
@@ -801,10 +775,6 @@
 }
 
 TimelineEvent* TimelineStream::StartEvent() {
-  if (Timeline::recorder_lock() == nullptr) {
-    return nullptr;
-  }
-  MutexLocker ml(Timeline::recorder_lock());
   TimelineEventRecorder* recorder = Timeline::recorder();
   if (!enabled() || (recorder == NULL)) {
     return NULL;
@@ -1167,7 +1137,6 @@
 }
 
 TimelineEventFixedBufferRecorder::~TimelineEventFixedBufferRecorder() {
-  MutexLocker ml(&lock_);
   // Delete all blocks.
   for (intptr_t i = 0; i < num_blocks_; i++) {
     blocks_[i].Reset();
@@ -1365,9 +1334,14 @@
     : head_(nullptr), tail_(nullptr), block_index_(0) {}
 
 TimelineEventEndlessRecorder::~TimelineEventEndlessRecorder() {
-  // Ensures block state is cleared for each thread.
-  Timeline::ReclaimCachedBlocksFromThreads();
-  Clear();
+  TimelineEventBlock* current = head_;
+  head_ = tail_ = nullptr;
+
+  while (current != nullptr) {
+    TimelineEventBlock* next = current->next();
+    delete current;
+    current = next;
+  }
 }
 
 #ifndef PRODUCT
@@ -1450,7 +1424,6 @@
 #endif
 
 void TimelineEventEndlessRecorder::Clear() {
-  MutexLocker ml(&lock_);
   TimelineEventBlock* current = head_;
   while (current != NULL) {
     TimelineEventBlock* next = current->next();
@@ -1458,8 +1431,9 @@
     current = next;
   }
   head_ = NULL;
-  tail_ = NULL;
   block_index_ = 0;
+  OSThread* thread = OSThread::Current();
+  thread->set_timeline_block(NULL);
 }
 
 TimelineEventBlock::TimelineEventBlock(intptr_t block_index)
diff --git a/runtime/vm/timeline.h b/runtime/vm/timeline.h
index a1eb5d6..38e739a 100644
--- a/runtime/vm/timeline.h
+++ b/runtime/vm/timeline.h
@@ -23,7 +23,7 @@
 #if defined(__MAC_10_14) || defined (__IPHONE_12_0)
 #define HOST_OS_SUPPORTS_SIGNPOST 1
 #endif
-// signpost.h exists in macOS 10.14, iOS 12 or above
+//signpost.h exists in macOS 10.14, iOS 12 or above
 #if defined(HOST_OS_SUPPORTS_SIGNPOST)
 #include <os/signpost.h>
 #else
@@ -123,18 +123,15 @@
 
 class Timeline : public AllStatic {
  public:
-  // Initialize timeline system.
+  // Initialize timeline system. Not thread safe.
   static void Init();
 
-  // Cleanup timeline system.
+  // Cleanup timeline system. Not thread safe.
   static void Cleanup();
 
-  // Access the global recorder. recorder_lock() must be held when accessing
-  // the returned |TimelineEventRecorder|.
+  // Access the global recorder. Not thread safe.
   static TimelineEventRecorder* recorder();
 
-  static Mutex* recorder_lock() { return recorder_lock_; }
-
   // Reclaim all |TimelineEventBlocks|s that are cached by threads.
   static void ReclaimCachedBlocksFromThreads();
 
@@ -161,13 +158,8 @@
 #undef TIMELINE_STREAM_FLAGS
 
  private:
-  // Reclaims all |TimelineEventBlocks|s that are cached by threads, assuming
-  // that lock_ is held.
-  static void ReclaimCachedBlocksFromThreadsLocked();
-
   static TimelineEventRecorder* recorder_;
   static MallocGrowableArray<char*>* enabled_streams_;
-  static Mutex* recorder_lock_;
 
 #define TIMELINE_STREAM_DECLARE(name, fuchsia_name)                            \
   static TimelineStream stream_##name##_;
diff --git a/runtime/vm/timeline_test.cc b/runtime/vm/timeline_test.cc
index ac1ce05..70d162e 100644
--- a/runtime/vm/timeline_test.cc
+++ b/runtime/vm/timeline_test.cc
@@ -90,6 +90,11 @@
     event->Complete();
   }
 
+  static void Clear(TimelineEventRecorder* recorder) {
+    ASSERT(recorder != NULL);
+    recorder->Clear();
+  }
+
   static void FinishBlock(TimelineEventBlock* block) { block->Finish(); }
 };
 
@@ -453,6 +458,7 @@
     EXPECT(!it.HasNext());
   }
 
+  TimelineTestHelper::Clear(recorder);
   delete recorder;
 }
 
@@ -489,6 +495,7 @@
   const char* beta = strstr(js.ToCString(), "Beta");
   EXPECT(alpha < beta);
 
+  TimelineTestHelper::Clear(recorder);
   delete recorder;
 }
 
@@ -513,8 +520,8 @@
     EXPECT_EQ(10, pauses.MaxInclusiveTime("a"));
     EXPECT_EQ(10, pauses.MaxExclusiveTime("a"));
   }
-  delete recorder;
-  recorder = new TimelineEventEndlessRecorder();
+  TimelineTestHelper::Clear(recorder);
+
   // Test case.
   TimelineTestHelper::FakeDuration(recorder, "a", 0, 10);
   TimelineTestHelper::FakeDuration(recorder, "b", 0, 10);
@@ -532,9 +539,7 @@
     EXPECT_EQ(10, pauses.MaxInclusiveTime("b"));
     EXPECT_EQ(10, pauses.MaxExclusiveTime("b"));
   }
-
-  delete recorder;
-  recorder = new TimelineEventEndlessRecorder();
+  TimelineTestHelper::Clear(recorder);
 
   // Test case.
   TimelineTestHelper::FakeDuration(recorder, "a", 0, 10);
@@ -553,8 +558,7 @@
     EXPECT_EQ(7, pauses.MaxInclusiveTime("b"));
     EXPECT_EQ(7, pauses.MaxExclusiveTime("b"));
   }
-  delete recorder;
-  recorder = new TimelineEventEndlessRecorder();
+  TimelineTestHelper::Clear(recorder);
 
   // Test case.
   TimelineTestHelper::FakeDuration(recorder, "a", 0, 10);
@@ -582,8 +586,7 @@
     EXPECT_EQ(1, pauses.MaxInclusiveTime("b"));
     EXPECT_EQ(1, pauses.MaxExclusiveTime("b"));
   }
-  delete recorder;
-  recorder = new TimelineEventEndlessRecorder();
+  TimelineTestHelper::Clear(recorder);
 
   // Test case.
   TimelineTestHelper::FakeDuration(recorder, "a", 0, 10);
@@ -613,8 +616,7 @@
     EXPECT_EQ(5, pauses.MaxInclusiveTime("d"));
     EXPECT_EQ(5, pauses.MaxExclusiveTime("d"));
   }
-  delete recorder;
-  recorder = new TimelineEventEndlessRecorder();
+  TimelineTestHelper::Clear(recorder);
 
   // Test case.
   TimelineTestHelper::FakeDuration(recorder, "a", 0, 10);
@@ -649,8 +651,7 @@
     EXPECT_EQ(2, pauses.MaxInclusiveTime("e"));
     EXPECT_EQ(2, pauses.MaxExclusiveTime("e"));
   }
-  delete recorder;
-  recorder = new TimelineEventEndlessRecorder();
+  TimelineTestHelper::Clear(recorder);
 
   // Test case.
   TimelineTestHelper::FakeDuration(recorder, "a", 0, 10);
@@ -666,6 +667,8 @@
     EXPECT_EQ(10, pauses.MaxInclusiveTime("a"));
     EXPECT_EQ(8, pauses.MaxExclusiveTime("a"));
   }
+  TimelineTestHelper::Clear(recorder);
+
   delete recorder;
 }
 
@@ -692,8 +695,7 @@
     EXPECT_EQ(10, pauses.MaxInclusiveTime("a"));
     EXPECT_EQ(10, pauses.MaxExclusiveTime("a"));
   }
-  delete recorder;
-  recorder = new TimelineEventEndlessRecorder();
+  TimelineTestHelper::Clear(recorder);
 
   // Test case.
   TimelineTestHelper::FakeBegin(recorder, "a", 0);
@@ -715,8 +717,7 @@
     EXPECT_EQ(10, pauses.MaxInclusiveTime("b"));
     EXPECT_EQ(10, pauses.MaxExclusiveTime("b"));
   }
-  delete recorder;
-  recorder = new TimelineEventEndlessRecorder();
+  TimelineTestHelper::Clear(recorder);
 
   // Test case.
   TimelineTestHelper::FakeBegin(recorder, "a", 0);
@@ -738,8 +739,7 @@
     EXPECT_EQ(7, pauses.MaxInclusiveTime("b"));
     EXPECT_EQ(7, pauses.MaxExclusiveTime("b"));
   }
-  delete recorder;
-  recorder = new TimelineEventEndlessRecorder();
+  TimelineTestHelper::Clear(recorder);
 
   // Test case.
   TimelineTestHelper::FakeBegin(recorder, "a", 0);
@@ -772,8 +772,7 @@
     EXPECT_EQ(1, pauses.MaxInclusiveTime("b"));
     EXPECT_EQ(1, pauses.MaxExclusiveTime("b"));
   }
-  delete recorder;
-  recorder = new TimelineEventEndlessRecorder();
+  TimelineTestHelper::Clear(recorder);
 
   // Test case.
   TimelineTestHelper::FakeBegin(recorder, "a", 0);
@@ -807,8 +806,7 @@
     EXPECT_EQ(5, pauses.MaxInclusiveTime("d"));
     EXPECT_EQ(5, pauses.MaxExclusiveTime("d"));
   }
-  delete recorder;
-  recorder = new TimelineEventEndlessRecorder();
+  TimelineTestHelper::Clear(recorder);
 
   // Test case.
   TimelineTestHelper::FakeBegin(recorder, "a", 0);
@@ -848,8 +846,7 @@
     EXPECT_EQ(2, pauses.MaxInclusiveTime("e"));
     EXPECT_EQ(2, pauses.MaxExclusiveTime("e"));
   }
-  delete recorder;
-  recorder = new TimelineEventEndlessRecorder();
+  TimelineTestHelper::Clear(recorder);
 
   // Test case.
   TimelineTestHelper::FakeBegin(recorder, "a", 0);
@@ -867,8 +864,7 @@
     EXPECT_EQ(10, pauses.MaxInclusiveTime("a"));
     EXPECT_EQ(8, pauses.MaxExclusiveTime("a"));
   }
-  delete recorder;
-  recorder = new TimelineEventEndlessRecorder();
+  TimelineTestHelper::Clear(recorder);
 
   // Test case.
   TimelineTestHelper::FakeBegin(recorder, "a", 0);
@@ -882,6 +878,8 @@
     pauses.CalculatePauseTimesForThread(tid);
     EXPECT(pauses.has_error());
   }
+  TimelineTestHelper::Clear(recorder);
+
   delete recorder;
 }
 
diff --git a/tools/VERSION b/tools/VERSION
index 5bfccf5..2c67925 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 13
 PATCH 0
-PRERELEASE 159
+PRERELEASE 160
 PRERELEASE_PATCH 0
\ No newline at end of file