Commit 6aae6764 authored by Ian Hickson's avatar Ian Hickson Committed by GitHub

Avoid returning null from debugDescribeChildren (#11806)

In some cases, the element tree is not clean but we are required to dump the tree anyway.
To avoid crashing in those cases, we return an explicit null node.
parent f1a23e26
......@@ -845,14 +845,10 @@ abstract class DiagnosticsNode {
for (int i = 0; i < children.length; i++) {
final DiagnosticsNode child = children[i];
assert(child != null);
final TextTreeConfiguration childConfig = _childTextConfiguration(child, config);
if (i == children.length - 1) {
final String lastChildPrefixLineOne = '$prefixChildren${childConfig.prefixLastChildLineOne}';
if (child == null) {
builder.writeRawLine('$lastChildPrefixLineOne<null>');
continue;
}
builder.writeRawLine(child.toStringDeep(
lastChildPrefixLineOne,
'$prefixChildren${childConfig.childLinkSpace}${childConfig.prefixOtherLines}',
......@@ -862,14 +858,8 @@ abstract class DiagnosticsNode {
builder.writeRaw('$prefixChildren${childConfig.childLinkSpace}${childConfig.footer}');
} else {
final TextTreeConfiguration nextChildStyle = _childTextConfiguration(children[i + 1], config);
final String childPrefixLineOne = '$prefixChildren${childConfig.prefixLineOne}';
final String childPrefixOtherLines ='$prefixChildren${nextChildStyle.linkCharacter}${childConfig.prefixOtherLines}';
if (child == null) {
builder.writeRawLine('$childPrefixLineOne<null>');
continue;
}
builder.writeRawLine(child.toStringDeep(childPrefixLineOne, childPrefixOtherLines));
if (childConfig.footer.isNotEmpty)
builder.writeRaw('$prefixChildren${nextChildStyle.linkCharacter}${childConfig.footer}');
......@@ -2139,13 +2129,18 @@ abstract class DiagnosticableTree extends Diagnosticable {
/// Returns a list of [DiagnosticsNode] objects describing this node's
/// children.
///
/// Children that are offstage should added with `style`
/// Children that are offstage should be added with `style` set to
/// [DiagnosticsTreeStyle.offstage] to indicate that they are offstage.
///
/// The list must not contain any null entries. If there are explicit null
/// children to report, consider [new DiagnosticsNode.message] or
/// [DiagnosticsProperty<Object>] as possible [DiagnosticsNode] objects to
/// provide.
///
/// See also:
///
/// * [RenderTable.debugDescribeChildren], which provides high quality custom
/// descriptions for child nodes.
/// descriptions for its child nodes.
///
/// Used by [toStringDeep], [toDiagnosticsNode] and [toStringShallow].
@protected
......
......@@ -353,8 +353,14 @@ class TextSpan extends DiagnosticableTree {
@override
List<DiagnosticsNode> debugDescribeChildren() {
return children == null ?
const <DiagnosticsNode>[] :
children.map((TextSpan child) => child?.toDiagnosticsNode()).toList();
if (children == null)
return const <DiagnosticsNode>[];
return children.map((TextSpan child) {
if (child != null) {
return child.toDiagnosticsNode();
} else {
return new DiagnosticsNode.message('<null child>');
}
}).toList();
}
}
......@@ -3326,7 +3326,11 @@ abstract class Element extends DiagnosticableTree implements BuildContext {
List<DiagnosticsNode> debugDescribeChildren() {
final List<DiagnosticsNode> children = <DiagnosticsNode>[];
visitChildren((Element child) {
children.add(child.toDiagnosticsNode());
if (child != null) {
children.add(child.toDiagnosticsNode());
} else {
children.add(new DiagnosticsNode.message('<null child>'));
}
});
return children;
}
......
......@@ -101,6 +101,7 @@ class _WidgetInspectorState extends State<WidgetInspector>
final List<DiagnosticsNode> children = object.debugDescribeChildren();
for (int i = children.length - 1; i >= 0; i -= 1) {
final DiagnosticsNode diagnostics = children[i];
assert(diagnostics != null);
if (diagnostics.style == DiagnosticsTreeStyle.offstage ||
diagnostics.value is! RenderObject)
continue;
......
......@@ -30,24 +30,24 @@ void main() {
expect(c1 == b2, isFalse);
});
test('TextSpan', () {
test('TextSpan toStringDeep', () {
final TextSpan test = const TextSpan(
text: 'a',
style: const TextStyle(
fontSize: 10.0
fontSize: 10.0,
),
children: const <TextSpan>[
const TextSpan(
text: 'b',
children: const <TextSpan>[
const TextSpan()
]
const TextSpan(),
],
),
null,
const TextSpan(
text: 'c'
text: 'c',
),
]
],
);
expect(test.toStringDeep(), equals(
'TextSpan:\n'
......@@ -58,7 +58,7 @@ void main() {
' "b"\n'
' TextSpan:\n'
' (empty)\n'
' <null>\n'
' <null child>\n'
' TextSpan:\n'
' "c"\n'
));
......
......@@ -506,4 +506,42 @@ void main() {
),
);
});
testWidgets('Element diagnostics with null child', (WidgetTester tester) async {
await tester.pumpWidget(new NullChildTest());
final NullChildElement test = tester.element<NullChildElement>(find.byType(NullChildTest));
test.includeChild = true;
expect(
tester.binding.renderViewElement.toStringDeep(),
equalsIgnoringHashCodes(
'[root](renderObject: RenderView#4a0f0)\n'
'└NullChildTest(dirty)\n'
' └<null child>\n',
),
);
test.includeChild = false;
});
}
class NullChildTest extends Widget {
@override
Element createElement() => new NullChildElement(this);
}
class NullChildElement extends Element {
NullChildElement(Widget widget) : super(widget);
bool includeChild = false;
@override
void visitChildren(ElementVisitor visitor) {
if (includeChild)
visitor(null);
}
@override
void forgetChild(Element child) { }
@override
void performRebuild() { }
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment