Flow analysis: additional debug support.

I've found these changes helpful as part of developing the new "field
promotion" feature.  These changes have no effect unless the
`FlowAnalysisDebug` class is used.

Bug: https://github.com/dart-lang/language/issues/2020
Change-Id: I7badadc14bf901e77b8c166920aedf902093d7e1
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/250220
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Commit-Queue: Paul Berry <paulberry@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 3256bdc..d91b11e 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
@@ -1032,6 +1032,10 @@
 class FlowAnalysisDebug<Node extends Object, Statement extends Node,
         Expression extends Object, Variable extends Object, Type extends Object>
     implements FlowAnalysis<Node, Statement, Expression, Variable, Type> {
+  static int _nextCallbackId = 0;
+
+  static Expando<String> _description = new Expando<String>();
+
   FlowAnalysis<Node, Statement, Expression, Variable, Type> _wrapped;
 
   bool _exceptionOccurred = false;
@@ -1538,16 +1542,18 @@
 
   @override
   Map<Type, NonPromotionReason> Function() whyNotPromoted(Expression target) {
-    return _wrap(
-        'whyNotPromoted($target)', () => _wrapped.whyNotPromoted(target),
+    return _wrap('whyNotPromoted($target)',
+        () => _trackWhyNotPromoted(_wrapped.whyNotPromoted(target)),
         isQuery: true);
   }
 
   @override
   Map<Type, NonPromotionReason> Function() whyNotPromotedImplicitThis(
       Type staticType) {
-    return _wrap('whyNotPromotedImplicitThis($staticType)',
-        () => _wrapped.whyNotPromotedImplicitThis(staticType),
+    return _wrap(
+        'whyNotPromotedImplicitThis($staticType)',
+        () => _trackWhyNotPromoted(
+            _wrapped.whyNotPromotedImplicitThis(staticType)),
         isQuery: true);
   }
 
@@ -1561,6 +1567,19 @@
   @override
   void _dumpState() => _wrapped._dumpState();
 
+  /// Wraps [callback] so that when it is called, the call (and its return
+  /// value) will be printed to the console.  Also registers the wrapped
+  /// callback in [_description] so that it will be given a unique identifier
+  /// when printed to the console.
+  Map<Type, NonPromotionReason> Function() _trackWhyNotPromoted(
+      Map<Type, NonPromotionReason> Function() callback) {
+    String callbackToString = '#CALLBACK${_nextCallbackId++}';
+    Map<Type, NonPromotionReason> Function() wrappedCallback =
+        () => _wrap('$callbackToString()', callback, isQuery: true);
+    _description[wrappedCallback] = callbackToString;
+    return wrappedCallback;
+  }
+
   T _wrap<T>(String description, T callback(),
       {bool isQuery: false, bool? isPure}) {
     isPure ??= isQuery;
@@ -1578,10 +1597,18 @@
       _wrapped._dumpState();
     }
     if (isQuery) {
-      print('  => $result');
+      print('  => ${_describe(result)}');
     }
     return result;
   }
+
+  static String _describe(Object? value) {
+    if (value != null && value is! String && value is! num && value is! bool) {
+      String? description = _description[value];
+      if (description != null) return description;
+    }
+    return value.toString();
+  }
 }
 
 /// An instance of the [FlowModel] class represents the information gathered by
@@ -2613,6 +2640,9 @@
   final Type type;
 
   ReferenceWithType(this.reference, this.type);
+
+  @override
+  String toString() => 'ReferenceWithType($reference, $type)';
 }
 
 /// Data structure representing a unique value that a variable might take on
@@ -2859,6 +2889,9 @@
     if (nonPromotionHistory != null) {
       parts.add('nonPromotionHistory: $nonPromotionHistory');
     }
+    if (properties.isNotEmpty) {
+      parts.add('properties: $properties');
+    }
     return 'VariableModel(${parts.join(', ')})';
   }
 
@@ -3358,6 +3391,9 @@
   }
 
   @override
+  String toString() => 'VariableReference($variable)';
+
+  @override
   VariableModel<Variable, Type>? _getInfo(
           Map<Variable?, VariableModel<Variable, Type>> variableInfo) =>
       variableInfo[variable];
@@ -5026,6 +5062,10 @@
   }
 
   @override
+  String toString() =>
+      '_PropertyGetReference($target, $propertyName, $propertyMember)';
+
+  @override
   VariableModel<Variable, Type>? _getInfo(
       Map<Variable?, VariableModel<Variable, Type>> variableInfo) {
     VariableModel<Variable, Type> targetInfo = target.getInfo(variableInfo);