Version 2.13.0-62.0.dev

Merge commit '1d191a137494dd399567696bb466fe8b4c11c248' into 'dev'
diff --git a/pkg/_fe_analyzer_shared/test/flow_analysis/why_not_promoted/data/nullable_expression_call_error.dart b/pkg/_fe_analyzer_shared/test/flow_analysis/why_not_promoted/data/nullable_expression_call_error.dart
new file mode 100644
index 0000000..c603def
--- /dev/null
+++ b/pkg/_fe_analyzer_shared/test/flow_analysis/why_not_promoted/data/nullable_expression_call_error.dart
@@ -0,0 +1,119 @@
+// Copyright (c) 2020, 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 `NullableExpressionCallError`, for which we wish to report "why not
+// promoted" context information.
+
+class C1 {
+  C2? bad;
+}
+
+class C2 {
+  void call() {}
+}
+
+instance_method_invocation(C1 c) {
+  if (c.bad == null) return;
+  c.bad
+      /*cfe.invoke: notPromoted(propertyNotPromoted(member:C1.bad))*/
+      ();
+}
+
+class C3 {
+  C4? ok;
+  C5? bad;
+}
+
+class C4 {}
+
+class C5 {}
+
+extension on C4? {
+  void call() {}
+}
+
+extension on C5 {
+  void call() {}
+}
+
+extension_invocation_method(C3 c) {
+  if (c.ok == null) return;
+  c.ok();
+  if (c.bad == null) return;
+  c.bad
+      /*cfe.invoke: notPromoted(propertyNotPromoted(member:C3.bad))*/
+      ();
+}
+
+class C6 {
+  C7? bad;
+}
+
+class C7 {
+  void Function() get call => () {};
+}
+
+instance_getter_invocation(C6 c) {
+  if (c.bad == null) return;
+  c.bad
+      /*cfe.invoke: notPromoted(propertyNotPromoted(member:C6.bad))*/
+      ();
+}
+
+class C8 {
+  C9? ok;
+  C10? bad;
+}
+
+class C9 {}
+
+class C10 {}
+
+extension on C9? {
+  void Function() get call => () {};
+}
+
+extension on C10 {
+  void Function() get call => () {};
+}
+
+extension_invocation_getter(C8 c) {
+  if (c.ok == null) return;
+  c.ok();
+  if (c.bad == null) return;
+  c.bad
+      /*cfe.invoke: notPromoted(propertyNotPromoted(member:C8.bad))*/
+      ();
+}
+
+class C11 {
+  void Function()? bad;
+}
+
+function_invocation(C11 c) {
+  if (c.bad == null) return;
+  c.bad
+      /*cfe.invoke: notPromoted(propertyNotPromoted(member:C11.bad))*/
+      ();
+}
+
+class C12 {
+  C13? bad;
+}
+
+class C13 {
+  void Function() foo;
+  C13(this.foo);
+}
+
+instance_field_invocation(C12 c) {
+  if (c.bad == null) return;
+  c.bad
+      .
+      /*analyzer.notPromoted(propertyNotPromoted(member:C12.bad))*/
+      foo
+      /*cfe.invoke: notPromoted(propertyNotPromoted(member:C12.bad))*/
+      ();
+}
diff --git a/pkg/_fe_analyzer_shared/test/flow_analysis/why_not_promoted/data/nullable_error_scenarios.dart b/pkg/_fe_analyzer_shared/test/flow_analysis/why_not_promoted/data/nullable_method_call_error.dart
similarity index 91%
rename from pkg/_fe_analyzer_shared/test/flow_analysis/why_not_promoted/data/nullable_error_scenarios.dart
rename to pkg/_fe_analyzer_shared/test/flow_analysis/why_not_promoted/data/nullable_method_call_error.dart
index 5797fa4..40cc053 100644
--- a/pkg/_fe_analyzer_shared/test/flow_analysis/why_not_promoted/data/nullable_error_scenarios.dart
+++ b/pkg/_fe_analyzer_shared/test/flow_analysis/why_not_promoted/data/nullable_method_call_error.dart
@@ -2,9 +2,9 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-// This test contains a test case for each kind of error that can arise from an
-// expression being nullable, for which we wish to report "why not promoted"
-// errors.
+// This test contains a test case for each condition that can lead to the front
+// end's `NullableMethodCallError`, for which we wish to report "why not
+// promoted" context information.
 
 class C {
   int? i;
diff --git a/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart b/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart
index 13d0970..2f2df5f 100644
--- a/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart
@@ -4736,7 +4736,8 @@
               propertyName.text, receiverType, inferrer.isNonNullableByDefault),
           read.fileOffset,
           propertyName.text.length,
-          context: inferrer.getWhyNotPromotedContext(receiver, read));
+          context: inferrer.getWhyNotPromotedContext(
+              receiver, inferrer.flowAnalysis?.whyNotPromoted(receiver), read));
     }
     return readResult;
   }
diff --git a/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart b/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart
index 3acf8bb..0c457c2 100644
--- a/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart
+++ b/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart
@@ -305,10 +305,8 @@
   /// Computes a list of context messages explaining why [receiver] was not
   /// promoted, to be used when reporting an error for a larger expression
   /// containing [receiver].  [expression] is the containing expression.
-  List<LocatedMessage> getWhyNotPromotedContext(
-      Expression receiver, Expression expression) {
-    Map<DartType, NonPromotionReason> whyNotPromoted =
-        flowAnalysis?.whyNotPromoted(receiver);
+  List<LocatedMessage> getWhyNotPromotedContext(Expression receiver,
+      Map<DartType, NonPromotionReason> whyNotPromoted, Expression expression) {
     List<LocatedMessage> context;
     if (whyNotPromoted != null && whyNotPromoted.isNotEmpty) {
       _WhyNotPromotedVisitor whyNotPromotedVisitor =
@@ -2755,12 +2753,22 @@
           implicitInvocationPropertyName: name);
 
       if (!isTopLevel && target.isNullable) {
+        // Handles cases like:
+        //   C? c;
+        //   c();
+        // where there is an extension on C defined as:
+        //   extension on C {
+        //     void Function() get call => () {};
+        //   }
+        List<LocatedMessage> context = getWhyNotPromotedContext(
+            receiver, flowAnalysis?.whyNotPromoted(receiver), staticInvocation);
         result = wrapExpressionInferenceResultInProblem(
             result,
             templateNullableExpressionCallError.withArguments(
                 receiverType, isNonNullableByDefault),
             fileOffset,
-            noLength);
+            noLength,
+            context: context);
       }
 
       return result;
@@ -2780,13 +2788,23 @@
 
       Expression replacement = result.applyResult(staticInvocation);
       if (!isTopLevel && target.isNullable) {
+        List<LocatedMessage> context = getWhyNotPromotedContext(
+            receiver, flowAnalysis?.whyNotPromoted(receiver), staticInvocation);
         if (isImplicitCall) {
+          // Handles cases like:
+          //   int? i;
+          //   i();
+          // where there is an extension:
+          //   extension on int {
+          //     void call() {}
+          //   }
           replacement = helper.wrapInProblem(
               replacement,
               templateNullableExpressionCallError.withArguments(
                   receiverType, isNonNullableByDefault),
               fileOffset,
-              noLength);
+              noLength,
+              context: context);
         } else {
           // Handles cases like:
           //   int? i;
@@ -2801,7 +2819,7 @@
                   name.text, receiverType, isNonNullableByDefault),
               fileOffset,
               name.text.length,
-              context: getWhyNotPromotedContext(receiver, staticInvocation));
+              context: context);
         }
       }
       return createNullAwareExpressionInferenceResult(
@@ -2865,13 +2883,19 @@
     }
     Expression replacement = result.applyResult(expression);
     if (!isTopLevel && target.isNullableCallFunction) {
+      List<LocatedMessage> context = getWhyNotPromotedContext(
+          receiver, flowAnalysis?.whyNotPromoted(receiver), expression);
       if (isImplicitCall) {
+        // Handles cases like:
+        //   void Function()? f;
+        //   f();
         replacement = helper.wrapInProblem(
             replacement,
             templateNullableExpressionCallError.withArguments(
                 receiverType, isNonNullableByDefault),
             fileOffset,
-            noLength);
+            noLength,
+            context: context);
       } else {
         // Handles cases like:
         //   void Function()? f;
@@ -2882,7 +2906,7 @@
                 callName.text, receiverType, isNonNullableByDefault),
             fileOffset,
             callName.text.length,
-            context: getWhyNotPromotedContext(receiver, expression));
+            context: context);
       }
     }
     // TODO(johnniwinther): Check that type arguments against the bounds.
@@ -3031,13 +3055,23 @@
 
     replacement = result.applyResult(replacement);
     if (!isTopLevel && target.isNullable) {
+      List<LocatedMessage> context = getWhyNotPromotedContext(
+          receiver, flowAnalysis?.whyNotPromoted(receiver), expression);
       if (isImplicitCall) {
+        // Handles cases like:
+        //   C? c;
+        //   c();
+        // Where C is defined as:
+        //   class C {
+        //     void call();
+        //   }
         replacement = helper.wrapInProblem(
             replacement,
             templateNullableExpressionCallError.withArguments(
                 receiverType, isNonNullableByDefault),
             fileOffset,
-            noLength);
+            noLength,
+            context: context);
       } else {
         // Handles cases like:
         //   int? i;
@@ -3048,7 +3082,7 @@
                 methodName.text, receiverType, isNonNullableByDefault),
             fileOffset,
             methodName.text.length,
-            context: getWhyNotPromotedContext(receiver, expression));
+            context: context);
       }
     }
 
@@ -3172,12 +3206,22 @@
     }
 
     if (!isTopLevel && target.isNullable) {
+      // Handles cases like:
+      //   C? c;
+      //   c.foo();
+      // Where C is defined as:
+      //   class C {
+      //     void Function() get foo => () {};
+      //   }
+      List<LocatedMessage> context = getWhyNotPromotedContext(receiver,
+          flowAnalysis?.whyNotPromoted(receiver), invocationResult.expression);
       invocationResult = wrapExpressionInferenceResultInProblem(
           invocationResult,
           templateNullableExpressionCallError.withArguments(
               receiverType, isNonNullableByDefault),
           fileOffset,
-          noLength);
+          noLength,
+          context: context);
     }
 
     if (!library.loader.target.backendTarget.supportsExplicitGetterCalls) {
@@ -3274,6 +3318,14 @@
       receiver = _hoist(receiver, receiverType, hoistedExpressions);
     }
 
+    Map<DartType, NonPromotionReason> whyNotPromotedInfo;
+    if (!isTopLevel && target.isNullable) {
+      // We won't report the error until later (after we have an
+      // invocationResult), but we need to gather "why not promoted" info now,
+      // before we tell flow analysis about the property get.
+      whyNotPromotedInfo = flowAnalysis?.whyNotPromoted(receiver);
+    }
+
     Name originalName = field.name;
     Expression originalReceiver = receiver;
     Member originalTarget = field;
@@ -3297,6 +3349,8 @@
           kind, originalReceiver, originalName,
           resultType: calleeType, interfaceTarget: originalTarget)
         ..fileOffset = fileOffset;
+      flowAnalysis.propertyGet(
+          originalPropertyGet, originalReceiver, originalName.name);
     } else {
       originalPropertyGet =
           new PropertyGet(originalReceiver, originalName, originalTarget)
@@ -3343,12 +3397,25 @@
     }
 
     if (!isTopLevel && target.isNullable) {
+      // Handles cases like:
+      //   C? c;
+      //   c.foo();
+      // Where C is defined as:
+      //   class C {
+      //     void Function() foo;
+      //     C(this.foo);
+      //   }
+      // TODO(paulberry): would it be better to report NullableMethodCallError
+      // in this scenario?
+      List<LocatedMessage> context = getWhyNotPromotedContext(
+          receiver, whyNotPromotedInfo, invocationResult.expression);
       invocationResult = wrapExpressionInferenceResultInProblem(
           invocationResult,
           templateNullableExpressionCallError.withArguments(
               receiverType, isNonNullableByDefault),
           fileOffset,
-          noLength);
+          noLength,
+          context: context);
     }
 
     if (!library.loader.target.backendTarget.supportsExplicitGetterCalls) {
diff --git a/tools/VERSION b/tools/VERSION
index 739aa1d..d028b8a 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 13
 PATCH 0
-PRERELEASE 61
+PRERELEASE 62
 PRERELEASE_PATCH 0
\ No newline at end of file