Repository: flutter
Issue: String interpolation that captures object references for debugging tools
Labels: c: new feature,tool,engine,dependency: dart,P2,team-engine,triaged-engine

String interpolation in Dart is a convenient way to write a debug string but it has the limitation that the output can only be a string with no way for debuggers to extract out the actual objects referenced to generate richer debugger view and crosslink to other debugging tools such as the widget inspector.

For example, on the web you could write
```
console.log('Element ', element, ' was clicked');
```
and have the element show as an interactive element that you could click on to view in the elements page of the chrome devtools.
If you instead wrote
```dart
print('Element $element was clicked');
``` 
you would get a non interactive string representation of the Element. However, string interpolation is far easier to read than a list of optional positional parameters separated by commas so it is far preferable to use string interpolation if the needed structure can be maintained.

Proposal: write a kernel transformer and a lint that operate on a blessed list of constructors and top level methods in package:flutter, extracting the object references out of the expressions and encoding the output as the existing flexible `DiagnosticsNode` structure already used by Flutter debugging tools. The toString() of the DiagnosticsNode will match the toString of the string literal but the DiagnosticsNode will also contain the list of all object references in the string interpolation expression. The constructors will still function without the transformer but will not capture any of the object references if run in that mode in a release build. Users cannot attach debuggers to release builds anyway.

A lint warning will trigger if the constructor is called with a value that is not a string literal as in that case there is no way to robustly extract out the object ids and we will not attempt to be clever and handle any cases where the value passed to the constructor is more than a string literal.

Example:
```dart
class ErrorMessage extends DiagnosticsNode {
   
   // Calls to this constructor are replaced in debug mode by a kernel transformer which captures
   // the contents of the string interpolation template. In release mode this object is identical to
   // Diagnostics.message(message).
   ErrorMessage(String message);
}
ErrorMessage('Element $element is color $color'); // Good. Captures references to the element and color object for use by debugging tools.
final String str = 'Element $element is color $color';
ErrorMessage(str); // lint error
ErrorMessage('Element $element' + 'is color $color'); // lint error because want to keep things simple.
ErrorMessage('Element $element'
                         'is color $color'); // good.

// Hypothetical method that logs objects to the console and to the console in IDEs.

debugLog(('Element $element is color $color'); // good
final String str = 'Element $element is color $color';
debugLog(str); // lint error

Function foo = debugLog; // lint warning as otherwise there could be calls to debugLog that we can't 
foo(str); // protected by the lint warning on the previous line.
```

If Dart string interpolation was a more powerful string templating feature this kernel transformer wouldn't be needed but given the current Dart language it is the most practical way to support the sorts of minimal string templates handy for debugging.

More complex examples:
```dart
    ErrorMessage('Curves must map 0.0 to near zero and 1.0 to near one but '
              '${activeCurve.runtimeType} mapped $t to $transformedValue, which '
              'is near $roundedTransformedValue.'
```

This example suggests the heuristic that we should treat foo.runtimeType as capturing an object reference to foo instead of foo.runtimeType as the runtimeType call was added to make the regular string output prettier.
We may also want to generate lint errors on cases that are ambiguous to resolve at first so we don't over promise what templates we can robustly handle.
For example: 
```dart
ErrorMessage('Button is ${button.onClick != null ? 'enabled' :  'disabled'}');
```
might be a lint error at first as we are unclear what object reference to include. Showing a reference to the onClick event handler might be surprising or it might be what the error message author expected.

Additional considerations: for some cases debugging tools will want to display a richer debugging view of the object inline. This should only be done if the richer object view can generally fit in a similar sized area as the toString such as in the case of an icon or a color. For cases where the visual debug view is much larger, additional techniques need to be considered to evaluate how to display the visual view of the object adjacent to the message.

See https://github.com/flutter/flutter/issues/27327
for a related flutter issue that motivated this bug.

Open questions:
Should special patterns like
```dart
ErrorMessage(
  'Some curve:'
  '  $curve'
);'
```
be treated as "markdown" syntax for fully opting into property style view of the object?
Essentially that expression could be interpreted as meaning `DiagnosticsProperty('Some curvet', curve)`
which opens up opting into all rich debugger views of an object rather than being limited the subset of debugger views of objects that place nicely with inline display.

For example:
This would be the way a curve property could be displayed if we are confident we should opt into a full property view: which ignoring my poor markdown skills would look something like this:
`Some curve`:  ![image](https://user-images.githubusercontent.com/1226812/52381734-854cba00-2a27-11e9-9e08-269608925859.png)