Flow analysis: track property get targets.

These targets are needed for "why not promoted" error messages, and
it's easier to have flow analysis keep track of them than to have the
analyzer and CFE try to reconstruct them at the time of reporting the
error.

Bug: https://github.com/dart-lang/sdk/issues/44898
Change-Id: Ia8ef4a7ce13cc30860e59b7369e6230d233e252d
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/193832
Commit-Queue: Paul Berry <paulberry@google.com>
Reviewed-by: Johnni Winther <johnniwinther@google.com>
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
diff --git a/pkg/_fe_analyzer_shared/lib/src/flow_analysis/flow_analysis.dart b/pkg/_fe_analyzer_shared/lib/src/flow_analysis/flow_analysis.dart
index e4c7261..bf501a0 100644
--- a/pkg/_fe_analyzer_shared/lib/src/flow_analysis/flow_analysis.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/flow_analysis/flow_analysis.dart
@@ -736,8 +736,13 @@
   /// expression to the left hand side of the `.`, and [propertyName] should be
   /// the identifier to the right hand side of the `.`.  [staticType] should be
   /// the static type of the value returned by the property get.
+  ///
+  /// [propertyMember] should be whatever data structure the client uses to keep
+  /// track of the field or property being accessed.  In the event of
+  /// non-promotion of a property get, this value can be retrieved from
+  /// [PropertyNotPromoted.propertyMember].
   void propertyGet(Expression wholeExpression, Expression target,
-      String propertyName, Type staticType);
+      String propertyName, Object? propertyMember, Type staticType);
 
   /// Retrieves the SSA node associated with [variable], or `null` if [variable]
   /// is not associated with an SSA node because it is write captured.  For
@@ -788,8 +793,13 @@
   /// the whole property get, and [propertyName] should be the name of the
   /// property being read.  [staticType] should be the static type of the value
   /// returned by the property get.
-  void thisOrSuperPropertyGet(
-      Expression expression, String propertyName, Type staticType);
+  ///
+  /// [propertyMember] should be whatever data structure the client uses to keep
+  /// track of the field or property being accessed.  In the event of
+  /// non-promotion of a property get, this value can be retrieved from
+  /// [PropertyNotPromoted.propertyMember].
+  void thisOrSuperPropertyGet(Expression expression, String propertyName,
+      Object? propertyMember, Type staticType);
 
   /// Call this method just before visiting the body of a "try/catch" statement.
   ///
@@ -1339,11 +1349,12 @@
 
   @override
   void propertyGet(Expression wholeExpression, Expression target,
-      String propertyName, Type staticType) {
+      String propertyName, Object? propertyMember, Type staticType) {
     _wrap(
-        'propertyGet($wholeExpression, $target, $propertyName, $staticType)',
+        'propertyGet($wholeExpression, $target, $propertyName, '
+        '$propertyMember, $staticType)',
         () => _wrapped.propertyGet(
-            wholeExpression, target, propertyName, staticType));
+            wholeExpression, target, propertyName, propertyMember, staticType));
   }
 
   @override
@@ -1378,12 +1389,13 @@
   }
 
   @override
-  void thisOrSuperPropertyGet(
-      Expression expression, String propertyName, Type staticType) {
+  void thisOrSuperPropertyGet(Expression expression, String propertyName,
+      Object? propertyMember, Type staticType) {
     _wrap(
-        'thisOrSuperPropertyGet($expression, $propertyName, $staticType)',
+        'thisOrSuperPropertyGet($expression, $propertyName, $propertyMember, '
+        '$staticType)',
         () => _wrapped.thisOrSuperPropertyGet(
-            expression, propertyName, staticType));
+            expression, propertyName, propertyMember, staticType));
   }
 
   @override
@@ -2334,12 +2346,17 @@
   /// The name of the property.
   final String propertyName;
 
+  /// The field or property being accessed.  This matches a `propertyMember`
+  /// value that was passed to either [FlowAnalysis.propertyGet] or
+  /// [FlowAnalysis.thisOrSuperPropertyGet].
+  final Object? propertyMember;
+
   /// The static type of the property at the time of the access.  This is the
   /// type that was passed to [FlowAnalysis.whyNotPromoted]; it is provided to
   /// the client as a convenience for ID testing.
   final Type staticType;
 
-  PropertyNotPromoted(this.propertyName, this.staticType);
+  PropertyNotPromoted(this.propertyName, this.propertyMember, this.staticType);
 
   @override
   String get documentationLink => 'http://dart.dev/go/non-promo-property';
@@ -2517,8 +2534,10 @@
 
   /// Creates a reference representing a get of a property called [propertyName]
   /// on the reference represented by `this`.
-  Reference<Variable, Type> propertyGet(String propertyName) =>
-      new _PropertyGetReference<Variable, Type>(this, propertyName);
+  Reference<Variable, Type> propertyGet(
+          String propertyName, Object? propertyMember) =>
+      new _PropertyGetReference<Variable, Type>(
+          this, propertyName, propertyMember);
 
   /// Stores info for this reference in [variableInfo].
   void storeInfo(Map<Variable?, VariableModel<Variable, Type>> variableInfo,
@@ -3952,14 +3971,14 @@
 
   @override
   void propertyGet(Expression wholeExpression, Expression target,
-      String propertyName, Type staticType) {
+      String propertyName, Object? propertyMember, Type staticType) {
     Reference<Variable, Type>? reference =
         _getExpressionReference(target)?.reference;
     if (reference != null) {
       _storeExpressionReference(
           wholeExpression,
           new ReferenceWithType<Variable, Type>(
-              reference.propertyGet(propertyName), staticType));
+              reference.propertyGet(propertyName, propertyMember), staticType));
     }
   }
 
@@ -4016,12 +4035,13 @@
   }
 
   @override
-  void thisOrSuperPropertyGet(
-      Expression expression, String propertyName, Type staticType) {
+  void thisOrSuperPropertyGet(Expression expression, String propertyName,
+      Object? propertyMember, Type staticType) {
     _storeExpressionReference(
         expression,
         new ReferenceWithType<Variable, Type>(
-            new _ThisReference<Variable, Type>().propertyGet(propertyName),
+            new _ThisReference<Variable, Type>()
+                .propertyGet(propertyName, propertyMember),
             staticType));
   }
 
@@ -4686,7 +4706,7 @@
 
   @override
   void propertyGet(Expression wholeExpression, Expression target,
-      String propertyName, Type staticType) {}
+      String propertyName, Object? propertyMember, Type staticType) {}
 
   @override
   SsaNode<Variable, Type>? ssaNodeForTesting(Variable variable) {
@@ -4706,8 +4726,8 @@
   void thisOrSuper(Expression expression, Type staticType) {}
 
   @override
-  void thisOrSuperPropertyGet(
-      Expression expression, String propertyName, Type staticType) {}
+  void thisOrSuperPropertyGet(Expression expression, String propertyName,
+      Object? propertyMember, Type staticType) {}
 
   @override
   void tryCatchStatement_bodyBegin() {}
@@ -4908,7 +4928,12 @@
   /// The name of the property.
   final String propertyName;
 
-  _PropertyGetReference(this.target, this.propertyName);
+  /// The field or property being accessed.  This matches a `propertyMember`
+  /// value that was passed to either [FlowAnalysis.propertyGet] or
+  /// [FlowAnalysis.thisOrSuperPropertyGet].
+  final Object? propertyMember;
+
+  _PropertyGetReference(this.target, this.propertyName, this.propertyMember);
 
   @override
   Map<Type, NonPromotionReason> Function() getNonPromotionReasons(
@@ -4920,7 +4945,8 @@
       return () {
         Map<Type, NonPromotionReason> result = <Type, NonPromotionReason>{};
         for (Type type in promotedTypes) {
-          result[type] = new PropertyNotPromoted(propertyName, staticType);
+          result[type] =
+              new PropertyNotPromoted(propertyName, propertyMember, staticType);
         }
         return result;
       };
diff --git a/pkg/_fe_analyzer_shared/test/mini_ast.dart b/pkg/_fe_analyzer_shared/test/mini_ast.dart
index 5e41049..fd0db4b 100644
--- a/pkg/_fe_analyzer_shared/test/mini_ast.dart
+++ b/pkg/_fe_analyzer_shared/test/mini_ast.dart
@@ -1510,7 +1510,7 @@
       Harness h, FlowAnalysis<Node, Statement, Expression, Var, Type> flow) {
     var targetType = target._visit(h, flow);
     var propertyType = h.getMember(targetType, propertyName);
-    flow.propertyGet(this, target, propertyName, propertyType);
+    flow.propertyGet(this, target, propertyName, propertyName, propertyType);
     return propertyType;
   }
 
@@ -1614,7 +1614,7 @@
   @override
   Type _visit(
       Harness h, FlowAnalysis<Node, Statement, Expression, Var, Type> flow) {
-    flow.thisOrSuperPropertyGet(this, propertyName, type);
+    flow.thisOrSuperPropertyGet(this, propertyName, propertyName, type);
     return type;
   }
 }
diff --git a/pkg/analyzer/lib/src/dart/resolver/assignment_expression_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/assignment_expression_resolver.dart
index aaaa182..3b63d6c 100644
--- a/pkg/analyzer/lib/src/dart/resolver/assignment_expression_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/assignment_expression_resolver.dart
@@ -121,8 +121,7 @@
       CompileTimeErrorCode.INVALID_ASSIGNMENT,
       right,
       [rightType, writeType],
-      _resolver.computeWhyNotPromotedMessages(
-          right, right, whyNotPromoted?.call()),
+      _resolver.computeWhyNotPromotedMessages(right, whyNotPromoted?.call()),
     );
   }
 
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 caff683..a6ab6e7 100644
--- a/pkg/analyzer/lib/src/dart/resolver/method_invocation_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/method_invocation_resolver.dart
@@ -807,7 +807,11 @@
         );
       }
       _resolver.flowAnalysis?.flow?.propertyGet(
-          functionExpression, target, node.methodName.name, getterReturnType);
+          functionExpression,
+          target,
+          node.methodName.name,
+          node.methodName.staticElement,
+          getterReturnType);
       functionExpression.staticType = targetType;
     }
 
diff --git a/pkg/analyzer/lib/src/dart/resolver/property_element_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/property_element_resolver.dart
index 8a64758..e2d5336 100644
--- a/pkg/analyzer/lib/src/dart/resolver/property_element_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/property_element_resolver.dart
@@ -203,8 +203,8 @@
       readElementRequested = readLookup.requested;
       if (readElementRequested is PropertyAccessorElement &&
           !readElementRequested.isStatic) {
-        _resolver.flowAnalysis?.flow?.thisOrSuperPropertyGet(
-            node, node.name, readElementRequested.returnType);
+        _resolver.flowAnalysis?.flow?.thisOrSuperPropertyGet(node, node.name,
+            readElementRequested, readElementRequested.returnType);
       }
       _resolver.checkReadOfNotAssignedLocalVariable(node, readElementRequested);
     }
@@ -373,7 +373,11 @@
       nameErrorEntity: propertyName,
     );
 
-    _resolver.flowAnalysis?.flow?.propertyGet(node, target, propertyName.name,
+    _resolver.flowAnalysis?.flow?.propertyGet(
+        node,
+        target,
+        propertyName.name,
+        result.getter,
         result.getter?.returnType ?? _typeSystem.typeProvider.dynamicType);
 
     if (hasRead && result.needsGetterError) {
@@ -653,6 +657,7 @@
             node,
             target,
             propertyName.name,
+            readElement,
             readElement?.returnType ?? _typeSystem.typeProvider.dynamicType);
       }
 
diff --git a/pkg/analyzer/lib/src/dart/resolver/type_property_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/type_property_resolver.dart
index cff8b7e..4ffbaaa 100644
--- a/pkg/analyzer/lib/src/dart/resolver/type_property_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/type_property_resolver.dart
@@ -130,11 +130,11 @@
       if (flow != null) {
         if (receiver != null) {
           messages = _resolver.computeWhyNotPromotedMessages(
-              receiver, nameErrorEntity, flow.whyNotPromoted(receiver)());
+              nameErrorEntity, flow.whyNotPromoted(receiver)());
         } else {
           var thisType = _resolver.thisType;
           if (thisType != null) {
-            messages = _resolver.computeWhyNotPromotedMessages(receiver,
+            messages = _resolver.computeWhyNotPromotedMessages(
                 nameErrorEntity, flow.whyNotPromotedImplicitThis(thisType)());
           }
         }
diff --git a/pkg/analyzer/lib/src/error/bool_expression_verifier.dart b/pkg/analyzer/lib/src/error/bool_expression_verifier.dart
index 43d2462..e56e3ad 100644
--- a/pkg/analyzer/lib/src/error/bool_expression_verifier.dart
+++ b/pkg/analyzer/lib/src/error/bool_expression_verifier.dart
@@ -57,7 +57,7 @@
             errorCode: CompileTimeErrorCode
                 .UNCHECKED_USE_OF_NULLABLE_VALUE_AS_CONDITION,
             messages: _resolver.computeWhyNotPromotedMessages(
-                expression, expression, whyNotPromoted?.call()));
+                expression, whyNotPromoted?.call()));
       } else {
         _errorReporter.reportErrorForNode(errorCode, expression, arguments);
       }
diff --git a/pkg/analyzer/lib/src/error/nullable_dereference_verifier.dart b/pkg/analyzer/lib/src/error/nullable_dereference_verifier.dart
index cf1f3b5..fb96d1b 100644
--- a/pkg/analyzer/lib/src/error/nullable_dereference_verifier.dart
+++ b/pkg/analyzer/lib/src/error/nullable_dereference_verifier.dart
@@ -76,8 +76,8 @@
 
     List<DiagnosticMessage>? messages;
     if (errorNode is Expression) {
-      messages = _resolver.computeWhyNotPromotedMessages(errorNode, errorNode,
-          _resolver.flowAnalysis?.flow?.whyNotPromoted(errorNode)());
+      messages = _resolver.computeWhyNotPromotedMessages(
+          errorNode, _resolver.flowAnalysis?.flow?.whyNotPromoted(errorNode)());
     }
     report(errorNode, receiverType, errorCode: errorCode, messages: messages);
     return true;
diff --git a/pkg/analyzer/lib/src/generated/error_detection_helpers.dart b/pkg/analyzer/lib/src/generated/error_detection_helpers.dart
index af745c8..ce69393 100644
--- a/pkg/analyzer/lib/src/generated/error_detection_helpers.dart
+++ b/pkg/analyzer/lib/src/generated/error_detection_helpers.dart
@@ -85,8 +85,8 @@
       }
       return;
     }
-    var messages = computeWhyNotPromotedMessages(
-        expression, expression, whyNotPromoted?.call());
+    var messages =
+        computeWhyNotPromotedMessages(expression, whyNotPromoted?.call());
     // report problem
     if (isConstConstructor) {
       // TODO(paulberry): this error should be based on the actual type of the
@@ -229,7 +229,6 @@
   /// [whyNotPromoted] should be the non-promotion details returned by the flow
   /// analysis engine.
   List<DiagnosticMessage> computeWhyNotPromotedMessages(
-      Expression? expression,
       SyntacticEntity errorEntity,
       Map<DartType, NonPromotionReason>? whyNotPromoted);
 
@@ -313,8 +312,7 @@
         errorCode,
         getErrorNode(expression),
         [actualStaticType, expectedStaticType],
-        computeWhyNotPromotedMessages(
-            expression, expression, whyNotPromoted?.call()),
+        computeWhyNotPromotedMessages(expression, whyNotPromoted?.call()),
       );
       return false;
     }
diff --git a/pkg/analyzer/lib/src/generated/error_verifier.dart b/pkg/analyzer/lib/src/generated/error_verifier.dart
index 9601c2e9..0d097e9 100644
--- a/pkg/analyzer/lib/src/generated/error_verifier.dart
+++ b/pkg/analyzer/lib/src/generated/error_verifier.dart
@@ -312,7 +312,6 @@
 
   @override
   List<DiagnosticMessage> computeWhyNotPromotedMessages(
-      Expression? expression,
       SyntacticEntity errorEntity,
       Map<DartType, NonPromotionReason>? whyNotPromoted) {
     return [];
diff --git a/pkg/analyzer/lib/src/generated/resolver.dart b/pkg/analyzer/lib/src/generated/resolver.dart
index 1a1918d..b878793 100644
--- a/pkg/analyzer/lib/src/generated/resolver.dart
+++ b/pkg/analyzer/lib/src/generated/resolver.dart
@@ -533,17 +533,13 @@
 
   @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) {
         var whyNotPromotedVisitor = _WhyNotPromotedVisitor(
-            source, expression, errorEntity, flowAnalysis!.dataForTesting);
+            source, errorEntity, flowAnalysis!.dataForTesting);
         if (typeSystem.isPotentiallyNullable(entry.key)) continue;
         var message = entry.value.accept(whyNotPromotedVisitor);
         if (message != null) {
@@ -3447,10 +3443,6 @@
             PromotableElement, DartType> {
   final Source source;
 
-  /// The expression that was not promoted, or `null` if the thing that was not
-  /// promoted was an implicit `this`.
-  final Expression? _expression;
-
   final SyntacticEntity _errorEntity;
 
   final FlowAnalysisDataForTesting? _dataForTesting;
@@ -3459,8 +3451,7 @@
 
   DartType? propertyType;
 
-  _WhyNotPromotedVisitor(
-      this.source, this._expression, this._errorEntity, this._dataForTesting);
+  _WhyNotPromotedVisitor(this.source, this._errorEntity, this._dataForTesting);
 
   @override
   DiagnosticMessage? visitDemoteViaExplicitWrite(
@@ -3480,18 +3471,7 @@
   @override
   DiagnosticMessage? visitPropertyNotPromoted(
       PropertyNotPromoted<DartType> reason) {
-    var expression = _expression;
-    Element? receiverElement;
-    if (expression is SimpleIdentifier) {
-      receiverElement = expression.staticElement;
-    } else if (expression is PropertyAccess) {
-      receiverElement = expression.propertyName.staticElement;
-    } else if (expression is PrefixedIdentifier) {
-      receiverElement = expression.identifier.staticElement;
-    } else {
-      assert(false,
-          'Unrecognized property access expression: ${expression.runtimeType}');
-    }
+    var receiverElement = reason.propertyMember;
     if (receiverElement is PropertyAccessorElement) {
       propertyReference = receiverElement;
       propertyType = reason.staticType;
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 89fdd5e..3458ef6 100644
--- a/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart
@@ -1480,7 +1480,6 @@
               replacement = inferrer.helper.buildProblem(
                   messageNullableSpreadError, receiver.fileOffset, 1,
                   context: inferrer.getWhyNotPromotedContext(
-                      receiver,
                       inferrer.flowAnalysis?.whyNotPromoted(receiver)(),
                       element,
                       (type) => !type.isPotentiallyNullable));
@@ -1550,7 +1549,6 @@
             replacement = inferrer.helper.buildProblem(
                 messageNullableSpreadError, receiver.fileOffset, 1,
                 context: inferrer.getWhyNotPromotedContext(
-                    receiver,
                     inferrer.flowAnalysis?.whyNotPromoted(receiver)(),
                     element,
                     (type) => !type.isPotentiallyNullable));
@@ -1988,7 +1986,6 @@
               Expression problem = inferrer.helper.buildProblem(
                   messageNullableSpreadError, receiver.fileOffset, 1,
                   context: inferrer.getWhyNotPromotedContext(
-                      receiver,
                       inferrer.flowAnalysis?.whyNotPromoted(receiver)(),
                       entry,
                       (type) => !type.isPotentiallyNullable));
@@ -2008,7 +2005,6 @@
                 receiver.fileOffset,
                 1,
                 context: inferrer.getWhyNotPromotedContext(
-                    receiver,
                     inferrer.flowAnalysis?.whyNotPromoted(receiver)(),
                     entry,
                     (type) => !type.isPotentiallyNullable));
@@ -2119,7 +2115,6 @@
             keyError = inferrer.helper.buildProblem(
                 messageNullableSpreadError, receiver.fileOffset, 1,
                 context: inferrer.getWhyNotPromotedContext(
-                    receiver,
                     inferrer.flowAnalysis?.whyNotPromoted(receiver)(),
                     entry,
                     (type) => !type.isPotentiallyNullable));
@@ -2884,8 +2879,9 @@
     }
 
     ExpressionInferenceResult readResult = _computePropertyGet(node.readOffset,
-        readReceiver, receiverType, node.propertyName, const UnknownType(),
-        isThisReceiver: node.receiver is ThisExpression);
+            readReceiver, receiverType, node.propertyName, const UnknownType(),
+            isThisReceiver: node.receiver is ThisExpression)
+        .expressionInferenceResult;
 
     Expression read = readResult.expression;
     DartType readType = readResult.inferredType;
@@ -2941,8 +2937,9 @@
     Expression writeReceiver = createVariableGet(receiverVariable);
 
     ExpressionInferenceResult readResult = _computePropertyGet(node.readOffset,
-        readReceiver, receiverType, node.propertyName, const UnknownType(),
-        isThisReceiver: node.receiver is ThisExpression);
+            readReceiver, receiverType, node.propertyName, const UnknownType(),
+            isThisReceiver: node.receiver is ThisExpression)
+        .expressionInferenceResult;
 
     reportNonNullableInNullAwareWarningIfNeeded(
         readResult.inferredType, "??=", node.readOffset);
@@ -4695,7 +4692,7 @@
   /// [typeContext] is used to create implicit generic tearoff instantiation
   /// if necessary. [isThisReceiver] must be set to `true` if the receiver is a
   /// `this` expression.
-  ExpressionInferenceResult _computePropertyGet(
+  PropertyGetInferenceResult _computePropertyGet(
       int fileOffset,
       Expression receiver,
       DartType receiverType,
@@ -4879,12 +4876,11 @@
           read.fileOffset,
           propertyName.text.length,
           context: inferrer.getWhyNotPromotedContext(
-              receiver,
               inferrer.flowAnalysis?.whyNotPromoted(receiver)(),
               read,
               (type) => !type.isPotentiallyNullable));
     }
-    return readResult;
+    return new PropertyGetInferenceResult(readResult, readTarget.member);
   }
 
   /// Creates a property set operation of [writeTarget] on [receiver] using
@@ -5216,12 +5212,13 @@
     DartType nonNullReceiverType = receiverType.toNonNull();
 
     ExpressionInferenceResult readResult = _computePropertyGet(
-        node.readOffset,
-        readReceiver,
-        nonNullReceiverType,
-        node.propertyName,
-        const UnknownType(),
-        isThisReceiver: node.receiver is ThisExpression);
+            node.readOffset,
+            readReceiver,
+            nonNullReceiverType,
+            node.propertyName,
+            const UnknownType(),
+            isThisReceiver: node.receiver is ThisExpression)
+        .expressionInferenceResult;
     Expression read = readResult.expression;
     DartType readType = readResult.inferredType;
 
@@ -5752,8 +5749,9 @@
     DartType nonNullReceiverType = receiverType.toNonNull();
 
     ExpressionInferenceResult readResult = _computePropertyGet(node.readOffset,
-        readReceiver, nonNullReceiverType, node.name, typeContext,
-        isThisReceiver: node.receiver is ThisExpression);
+            readReceiver, nonNullReceiverType, node.name, typeContext,
+            isThisReceiver: node.receiver is ThisExpression)
+        .expressionInferenceResult;
     Expression read = readResult.expression;
     DartType readType = readResult.inferredType;
     inferrer.flowAnalysis.ifNullExpression_rightBegin(read, readType);
@@ -5845,11 +5843,13 @@
     DartType receiverType = result.nullAwareActionType;
 
     node.receiver = receiver..parent = node;
-    ExpressionInferenceResult readResult = _computePropertyGet(
+    PropertyGetInferenceResult propertyGetInferenceResult = _computePropertyGet(
         node.fileOffset, receiver, receiverType, node.name, typeContext,
         isThisReceiver: node.receiver is ThisExpression);
-    inferrer.flowAnalysis.propertyGet(
-        node, node.receiver, node.name.text, readResult.inferredType);
+    ExpressionInferenceResult readResult =
+        propertyGetInferenceResult.expressionInferenceResult;
+    inferrer.flowAnalysis.propertyGet(node, node.receiver, node.name.text,
+        propertyGetInferenceResult.member, readResult.inferredType);
     ExpressionInferenceResult expressionInferenceResult =
         inferrer.createNullAwareExpressionInferenceResult(
             readResult.inferredType, readResult.expression, nullAwareGuards);
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 15606b8..463c05b 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
@@ -290,14 +290,13 @@
   /// promoted, to be used when reporting an error for a larger expression
   /// containing [receiver].  [node] is the containing tree node.
   List<LocatedMessage> getWhyNotPromotedContext(
-      Expression receiver,
       Map<DartType, NonPromotionReason> whyNotPromoted,
       TreeNode node,
       bool Function(DartType) typeFilter) {
     List<LocatedMessage> context;
     if (whyNotPromoted != null && whyNotPromoted.isNotEmpty) {
       _WhyNotPromotedVisitor whyNotPromotedVisitor =
-          new _WhyNotPromotedVisitor(this, receiver);
+          new _WhyNotPromotedVisitor(this);
       for (core.MapEntry<DartType, NonPromotionReason> entry
           in whyNotPromoted.entries) {
         if (!typeFilter(entry.key)) continue;
@@ -615,7 +614,6 @@
                 nullabilityErrorTemplate.withArguments(expressionType,
                     declaredContextType ?? contextType, isNonNullableByDefault),
                 context: getWhyNotPromotedContext(
-                    expression,
                     flowAnalysis?.whyNotPromoted(expression)(),
                     expression,
                     (type) => typeSchemaEnvironment.isSubtypeOf(type,
@@ -2826,7 +2824,6 @@
         //     void Function() get call => () {};
         //   }
         List<LocatedMessage> context = getWhyNotPromotedContext(
-            receiver,
             flowAnalysis?.whyNotPromoted(receiver)(),
             staticInvocation,
             (type) => !type.isPotentiallyNullable);
@@ -2858,7 +2855,6 @@
       Expression replacement = result.applyResult(staticInvocation);
       if (!isTopLevel && target.isNullable) {
         List<LocatedMessage> context = getWhyNotPromotedContext(
-            receiver,
             flowAnalysis?.whyNotPromoted(receiver)(),
             staticInvocation,
             (type) => !type.isPotentiallyNullable);
@@ -2972,7 +2968,6 @@
     Expression replacement = result.applyResult(expression);
     if (!isTopLevel && target.isNullableCallFunction) {
       List<LocatedMessage> context = getWhyNotPromotedContext(
-          receiver,
           flowAnalysis?.whyNotPromoted(receiver)(),
           expression,
           (type) => !type.isPotentiallyNullable);
@@ -3147,7 +3142,6 @@
     replacement = result.applyResult(replacement);
     if (!isTopLevel && target.isNullable) {
       List<LocatedMessage> context = getWhyNotPromotedContext(
-          receiver,
           flowAnalysis?.whyNotPromoted(receiver)(),
           expression,
           (type) => !type.isPotentiallyNullable);
@@ -3308,7 +3302,6 @@
       //     void Function() get foo => () {};
       //   }
       List<LocatedMessage> context = getWhyNotPromotedContext(
-          receiver,
           flowAnalysis?.whyNotPromoted(receiver)(),
           invocationResult.expression,
           (type) => !type.isPotentiallyNullable);
@@ -3465,8 +3458,8 @@
           new PropertyGet(originalReceiver, originalName, originalTarget)
             ..fileOffset = fileOffset;
     }
-    flowAnalysis.propertyGet(
-        originalPropertyGet, originalReceiver, originalName.text, calleeType);
+    flowAnalysis.propertyGet(originalPropertyGet, originalReceiver,
+        originalName.text, originalTarget, calleeType);
     Expression propertyGet = originalPropertyGet;
     if (receiver is! ThisExpression &&
         calleeType is! DynamicType &&
@@ -3518,11 +3511,8 @@
       //   }
       // TODO(paulberry): would it be better to report NullableMethodCallError
       // in this scenario?
-      List<LocatedMessage> context = getWhyNotPromotedContext(
-          receiver,
-          whyNotPromoted(),
-          invocationResult.expression,
-          (type) => !type.isPotentiallyNullable);
+      List<LocatedMessage> context = getWhyNotPromotedContext(whyNotPromoted(),
+          invocationResult.expression, (type) => !type.isPotentiallyNullable);
       invocationResult = wrapExpressionInferenceResultInProblem(
           invocationResult,
           templateNullableExpressionCallError.withArguments(
@@ -3853,7 +3843,7 @@
       return instantiateTearOff(inferredType, typeContext, expression);
     }
     flowAnalysis.thisOrSuperPropertyGet(
-        expression, expression.name.name, inferredType);
+        expression, expression.name.name, member, inferredType);
     return new ExpressionInferenceResult(inferredType, expression);
   }
 
@@ -4644,6 +4634,17 @@
   }
 }
 
+/// The result of inference of a property get expression.
+class PropertyGetInferenceResult {
+  /// The main inference result.
+  final ExpressionInferenceResult expressionInferenceResult;
+
+  /// The property that was looked up, or `null` if no property was found.
+  final Member member;
+
+  PropertyGetInferenceResult(this.expressionInferenceResult, this.member);
+}
+
 /// The result of an expression inference.
 class ExpressionInferenceResult {
   /// The inferred type of the expression.
@@ -5134,13 +5135,11 @@
             DartType> {
   final TypeInferrerImpl inferrer;
 
-  final Expression receiver;
-
   Member propertyReference;
 
   DartType propertyType;
 
-  _WhyNotPromotedVisitor(this.inferrer, this.receiver);
+  _WhyNotPromotedVisitor(this.inferrer);
 
   @override
   LocatedMessage visitDemoteViaExplicitWrite(
@@ -5158,26 +5157,16 @@
 
   @override
   LocatedMessage visitPropertyNotPromoted(PropertyNotPromoted reason) {
-    Member member;
-    Expression receiver = this.receiver;
-    if (receiver is InstanceGet) {
-      member = receiver.interfaceTarget;
-    } else if (receiver is SuperPropertyGet) {
-      member = receiver.interfaceTarget;
-    } else if (receiver is StaticInvocation) {
-      member = receiver.target;
-    } else if (receiver is PropertyGet) {
-      member = receiver.interfaceTarget;
-    } else {
-      assert(false, 'Unrecognized receiver: ${receiver.runtimeType}');
-    }
-    if (member != null) {
+    Object member = reason.propertyMember;
+    if (member is Member) {
       propertyReference = member;
       propertyType = reason.staticType;
       return templateFieldNotPromoted
           .withArguments(reason.propertyName, reason.documentationLink)
           .withLocation(member.fileUri, member.fileOffset, noLength);
     } else {
+      assert(member == null,
+          'Unrecognized property member: ${member.runtimeType}');
       return null;
     }
   }