Skip to content

Commit

Permalink
[flutter_tools] Include more details in structured errors sent to a D…
Browse files Browse the repository at this point in the history
…AP client (flutter#150698)

The debug adapter converts Flutter's structured errors into a text format to be sent to the debug client and shown in the console. When an error is not the first error since the last reload, it is shown as just a summary (since it may be caused by a prior error). In this mode, the filter was causing some important information (the erroring widget) to be omitted.

This tweaks the logic to include child nodes of a `DiagnosticBlock` in this mode.

Fixes Dart-Code/Dart-Code#4743

## Before:

![image](https://github.com/flutter/flutter/assets/1078012/46ccd2ef-b165-46b4-a8ab-4473f82a904c)

## After:

![image](https://github.com/flutter/flutter/assets/1078012/232f866e-cf6f-4016-9d1d-49323204da04)
  • Loading branch information
DanTup authored Jun 28, 2024
1 parent ed8dde8 commit eb58fe1
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,11 @@ class FlutterErrorFormatter {
final bool allChildrenAreLeaf = node.children.isNotEmpty &&
!node.children.any((_ErrorNode child) => child.children.isNotEmpty);
if (node.level == _DiagnosticsNodeLevel.summary || allChildrenAreLeaf) {
_writeNode(node, recursive: false);
// DiagnosticsBlock is a container, so recurse into its children if
// there's only a single level. The container may be
// "The relevant error-causing widget was" and the child may be
// the specific widget details.
_writeNode(node, recursive: node.type == _DiagnosticsNodeType.DiagnosticsBlock && allChildrenAreLeaf);
}
}
}
Expand Down Expand Up @@ -148,6 +152,10 @@ enum _DiagnosticsNodeStyle {
flat,
}

enum _DiagnosticsNodeType {
DiagnosticsBlock,
}

class _ErrorData extends _ErrorNode {
_ErrorData(super.data);

Expand All @@ -167,6 +175,7 @@ class _ErrorNode {
List<_ErrorNode> get properties => asList('properties', _ErrorNode.new);
bool get showName => data['showName'] != false;
_DiagnosticsNodeStyle? get style => asEnum('style', _DiagnosticsNodeStyle.values);
_DiagnosticsNodeType? get type => asEnum('type', _DiagnosticsNodeType.values);

String? asString(String field) {
final Object? value = data[field];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@
// found in the LICENSE file.

import 'dart:async';
import 'dart:convert';

import 'package:dds/dap.dart';
import 'package:file/memory.dart';
import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/base/platform.dart';
import 'package:flutter_tools/src/cache.dart';
import 'package:flutter_tools/src/debug_adapters/error_formatter.dart';
import 'package:flutter_tools/src/debug_adapters/flutter_adapter.dart';
import 'package:flutter_tools/src/debug_adapters/flutter_adapter_args.dart';
import 'package:flutter_tools/src/globals.dart' as globals show fs, platform;
Expand Down Expand Up @@ -797,6 +799,55 @@ void main() {
expect(adapter.processArgs, contains('tool_args'));
});
});

group('error formatter', () {
/// Helpers to build a string representation of the DAP OutputEvents for
/// the structured error [errorData].
String getFormattedError(Map<String, Object?> errorData) {
// Format the error and write into a buffer in a text format convenient
// for test expectations.
final StringBuffer buffer = StringBuffer();
FlutterErrorFormatter()
..formatError(errorData)
..sendOutput((String category, String message, {bool? parseStackFrames, int? variablesReference}) {
buffer.writeln('${category.padRight(6)} ${jsonEncode(message)}');
});
return buffer.toString();
}

test('includes children of DiagnosticsBlock when writing a summary', () {
// Format a simulated error that nests the error-causing widget in a
// diagnostic block and will be displayed in summary mode (because it
// is not the first error since the last reload).
// https://github.com/Dart-Code/Dart-Code/issues/4743
final String error = getFormattedError(<String, Object?>{
'errorsSinceReload': 1, // Force summary mode
'description': 'Exception caught...',
'properties': <Map<String, Object?>>[
<String, Object>{
'description': 'The following assertion was thrown...',
},
<String, Object?>{
'description': '',
'type': 'DiagnosticsBlock',
'name': 'The relevant error-causing widget was',
'children': <Map<String, Object>>[
<String, Object>{
'description': 'MyWidget:file:///path/to/widget.dart:1:2',
}
]
}
],
});

expect(error, r'''
stdout "\n"
stderr "════════ Exception caught... ═══════════════════════════════════════════════════\n"
stdout "The relevant error-causing widget was:\n MyWidget:file:///path/to/widget.dart:1:2\n"
stderr "════════════════════════════════════════════════════════════════════════════════\n"
''');
});
});
});
}

Expand Down

0 comments on commit eb58fe1

Please sign in to comment.