Provide details when reporting invalid constraints (#3281)
This also shrinks the width of the error messages a bit because now that we use 'package:' URLs the stacks are a bit narrower.
This commit is contained in:
parent
3525aa1b0a
commit
ecf1cce82c
@ -43,7 +43,10 @@ class SectorConstraints extends Constraints {
|
||||
bool get isNormalized => minDeltaRadius <= maxDeltaRadius && minDeltaTheta <= maxDeltaTheta;
|
||||
|
||||
@override
|
||||
bool debugAssertIsValid({ bool isAppliedConstraint: false }) {
|
||||
bool debugAssertIsValid({
|
||||
bool isAppliedConstraint: false,
|
||||
InformationCollector informationCollector
|
||||
}) {
|
||||
assert(isNormalized);
|
||||
return isNormalized;
|
||||
}
|
||||
|
@ -134,7 +134,7 @@ class FlutterErrorDetailsForPointerEventDispatcher extends FlutterErrorDetails {
|
||||
String context,
|
||||
this.event,
|
||||
this.hitTestEntry,
|
||||
FlutterInformationCollector informationCollector,
|
||||
InformationCollector informationCollector,
|
||||
bool silent: false
|
||||
}) : super(
|
||||
exception: exception,
|
||||
|
@ -89,7 +89,7 @@ class FlutterErrorDetailsForPointerRouter extends FlutterErrorDetails {
|
||||
this.router,
|
||||
this.route,
|
||||
this.event,
|
||||
FlutterInformationCollector informationCollector,
|
||||
InformationCollector informationCollector,
|
||||
bool silent: false
|
||||
}) : super(
|
||||
exception: exception,
|
||||
|
@ -303,8 +303,17 @@ class BoxConstraints extends Constraints {
|
||||
}
|
||||
|
||||
@override
|
||||
bool debugAssertIsValid({ bool isAppliedConstraint: false }) {
|
||||
bool debugAssertIsValid({
|
||||
bool isAppliedConstraint: false,
|
||||
InformationCollector informationCollector
|
||||
}) {
|
||||
assert(() {
|
||||
void throwError(String message) {
|
||||
StringBuffer information = new StringBuffer();
|
||||
if (informationCollector != null)
|
||||
informationCollector(information);
|
||||
throw new FlutterError('$message\n${information}The offending constraints were:\n $this');
|
||||
}
|
||||
if (minWidth.isNaN || maxWidth.isNaN || minHeight.isNaN || maxHeight.isNaN) {
|
||||
List<String> affectedFieldsList = <String>[];
|
||||
if (minWidth.isNaN)
|
||||
@ -326,27 +335,27 @@ class BoxConstraints extends Constraints {
|
||||
} else {
|
||||
whichFields = affectedFieldsList.single;
|
||||
}
|
||||
throw new FlutterError('BoxConstraints has ${affectedFieldsList.length == 1 ? 'a NaN value' : 'NaN values' } in $whichFields.\n$this');
|
||||
throwError('BoxConstraints has ${affectedFieldsList.length == 1 ? 'a NaN value' : 'NaN values' } in $whichFields.');
|
||||
}
|
||||
if (minWidth < 0.0 && minHeight < 0.0)
|
||||
throw new FlutterError('BoxConstraints has both a negative minimum width and a negative minimum height.\n$this');
|
||||
throwError('BoxConstraints has both a negative minimum width and a negative minimum height.');
|
||||
if (minWidth < 0.0)
|
||||
throw new FlutterError('BoxConstraints has a negative minimum width.\n$this');
|
||||
throwError('BoxConstraints has a negative minimum width.');
|
||||
if (minHeight < 0.0)
|
||||
throw new FlutterError('BoxConstraints has a negative minimum height.\n$this');
|
||||
throwError('BoxConstraints has a negative minimum height.');
|
||||
if (maxWidth < minWidth && maxHeight < minHeight)
|
||||
throw new FlutterError('BoxConstraints has both width and height constraints non-normalized.\n$this');
|
||||
throwError('BoxConstraints has both width and height constraints non-normalized.');
|
||||
if (maxWidth < minWidth)
|
||||
throw new FlutterError('BoxConstraints has non-normalized width constraints.\n$this');
|
||||
throwError('BoxConstraints has non-normalized width constraints.');
|
||||
if (maxHeight < minHeight)
|
||||
throw new FlutterError('BoxConstraints has non-normalized height constraints.\n$this');
|
||||
throwError('BoxConstraints has non-normalized height constraints.');
|
||||
if (isAppliedConstraint) {
|
||||
if (minWidth.isInfinite && minHeight.isInfinite)
|
||||
throw new FlutterError('BoxConstraints requires infinite width and infinite height.\n$this');
|
||||
throwError('BoxConstraints forces an infinite width and infinite height.');
|
||||
if (minWidth.isInfinite)
|
||||
throw new FlutterError('BoxConstraints requires infinite width.\n$this');
|
||||
throwError('BoxConstraints forces an infinite width.');
|
||||
if (minHeight.isInfinite)
|
||||
throw new FlutterError('BoxConstraints requires infinite height.\n$this');
|
||||
throwError('BoxConstraints forces an infinite height.');
|
||||
}
|
||||
assert(isNormalized);
|
||||
return true;
|
||||
|
@ -20,7 +20,7 @@ import 'binding.dart';
|
||||
|
||||
export 'package:flutter/gestures.dart' show HitTestEntry, HitTestResult;
|
||||
export 'package:flutter/painting.dart';
|
||||
export 'package:flutter/services.dart' show FlutterError;
|
||||
export 'package:flutter/services.dart' show FlutterError, InformationCollector;
|
||||
|
||||
/// Base class for data associated with a [RenderObject] by its parent.
|
||||
///
|
||||
@ -389,7 +389,7 @@ abstract class Constraints {
|
||||
/// For example, the [BoxConstraints] subclass verifies that the
|
||||
/// constraints are not [NaN].
|
||||
///
|
||||
/// If the [isAppliedConstraint] argument is true, then even
|
||||
/// If the `isAppliedConstraint` argument is true, then even
|
||||
/// stricter rules are enforced. This argument is set to true when
|
||||
/// checking constraints that are about to be applied to a
|
||||
/// [RenderObject] during layout, as opposed to constraints that may
|
||||
@ -399,8 +399,16 @@ abstract class Constraints {
|
||||
/// argument, but the asserts for verifying the argument passed to
|
||||
/// the [layout] method do.
|
||||
///
|
||||
/// The `informationCollector` argument takes an optional callback
|
||||
/// which is called when an exception is to be thrown. The collected
|
||||
/// information is then included in the message after the error
|
||||
/// line.
|
||||
///
|
||||
/// Returns the same as [isNormalized] if asserts are disabled.
|
||||
bool debugAssertIsValid({ bool isAppliedConstraint: false });
|
||||
bool debugAssertIsValid({
|
||||
bool isAppliedConstraint: false,
|
||||
InformationCollector informationCollector
|
||||
});
|
||||
}
|
||||
|
||||
typedef void RenderObjectVisitor(RenderObject child);
|
||||
@ -900,13 +908,13 @@ abstract class RenderObject extends AbstractNode implements HitTestTarget {
|
||||
context: 'during $method()',
|
||||
renderObject: this,
|
||||
informationCollector: (StringBuffer information) {
|
||||
information.writeln('The following RenderObject was being processed when the exception was fired:\n${this}');
|
||||
information.writeln('The following RenderObject was being processed when the exception was fired:\n $this');
|
||||
if (debugCreator != null)
|
||||
information.writeln('This RenderObject had the following creator:\n $debugCreator');
|
||||
information.writeln('This RenderObject had the following creator information:\n $debugCreator');
|
||||
List<String> descendants = <String>[];
|
||||
const int maxDepth = 5;
|
||||
int depth = 0;
|
||||
const int maxLines = 30;
|
||||
const int maxLines = 25;
|
||||
int lines = 0;
|
||||
void visitor(RenderObject child) {
|
||||
if (lines < maxLines) {
|
||||
@ -1147,12 +1155,39 @@ abstract class RenderObject extends AbstractNode implements HitTestTarget {
|
||||
/// override performResize and/or performLayout.
|
||||
///
|
||||
/// The parent's performLayout method should call the layout of all its
|
||||
/// children unconditionally. It is the layout functions's responsibility (as
|
||||
/// children unconditionally. It is the layout function's responsibility (as
|
||||
/// implemented here) to return early if the child does not need to do any
|
||||
/// work to update its layout information.
|
||||
void layout(Constraints constraints, { bool parentUsesSize: false }) {
|
||||
assert(constraints != null);
|
||||
assert(constraints.debugAssertIsValid(isAppliedConstraint: true));
|
||||
assert(constraints.debugAssertIsValid(
|
||||
isAppliedConstraint: true,
|
||||
informationCollector: (StringBuffer information) {
|
||||
List<String> stack = StackTrace.current.toString().split('\n');
|
||||
int targetFrame;
|
||||
Pattern layoutFramePattern = new RegExp(r'^#[0-9]+ +RenderObject.layout \(');
|
||||
for (int i = 0; i < stack.length; i += 1) {
|
||||
if (layoutFramePattern.matchAsPrefix(stack[i]) != null) {
|
||||
targetFrame = i + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (targetFrame != null && targetFrame < stack.length) {
|
||||
information.writeln(
|
||||
'These invalid constraints were provided to $runtimeType\'s method() '
|
||||
'function by the following function, which probably computed the '
|
||||
'invalid constraints in question:'
|
||||
);
|
||||
Pattern targetFramePattern = new RegExp(r'^#[0-9]+ +(.+)$');
|
||||
Match targetFrameMatch = targetFramePattern.matchAsPrefix(stack[targetFrame]);
|
||||
if (targetFrameMatch != null && targetFrameMatch.groupCount > 0) {
|
||||
information.writeln(' ${targetFrameMatch.group(1)}');
|
||||
} else {
|
||||
information.writeln(stack[targetFrame]);
|
||||
}
|
||||
}
|
||||
}
|
||||
));
|
||||
assert(!_debugDoingThisResize);
|
||||
assert(!_debugDoingThisLayout);
|
||||
final RenderObject parent = this.parent;
|
||||
@ -2189,7 +2224,7 @@ class FlutterErrorDetailsForRendering extends FlutterErrorDetails {
|
||||
String library,
|
||||
String context,
|
||||
this.renderObject,
|
||||
FlutterInformationCollector informationCollector,
|
||||
InformationCollector informationCollector,
|
||||
bool silent: false
|
||||
}) : super(
|
||||
exception: exception,
|
||||
|
@ -7,11 +7,9 @@ import 'print.dart';
|
||||
/// Signature for [FlutterError.onException] handler.
|
||||
typedef void FlutterExceptionHandler(FlutterErrorDetails details);
|
||||
|
||||
/// Signature for [FlutterErrorDetails.informationCollector] callback.
|
||||
///
|
||||
/// The text written to the information argument may contain newlines but should
|
||||
/// not end with a newline.
|
||||
typedef void FlutterInformationCollector(StringBuffer information);
|
||||
/// Signature for [FlutterErrorDetails.informationCollector] callback
|
||||
/// and other callbacks that collect information into a string buffer.
|
||||
typedef void InformationCollector(StringBuffer information);
|
||||
|
||||
/// Class for information provided to [FlutterExceptionHandler] callbacks.
|
||||
///
|
||||
@ -56,7 +54,10 @@ class FlutterErrorDetails {
|
||||
///
|
||||
/// Information collector callbacks can be expensive, so the generated information
|
||||
/// should be cached, rather than the callback being invoked multiple times.
|
||||
final FlutterInformationCollector informationCollector;
|
||||
///
|
||||
/// The text written to the information argument may contain newlines but should
|
||||
/// not end with a newline.
|
||||
final InformationCollector informationCollector;
|
||||
|
||||
/// Whether this error should be ignored by the default error reporting
|
||||
/// behavior in release mode.
|
||||
@ -124,7 +125,7 @@ class FlutterError extends AssertionError {
|
||||
|
||||
static int _errorCount = 0;
|
||||
|
||||
static const int _kWrapWidth = 120;
|
||||
static const int _kWrapWidth = 100;
|
||||
|
||||
/// Prints the given exception details to the console.
|
||||
///
|
||||
|
@ -43,7 +43,8 @@ void main() {
|
||||
}
|
||||
expect(result, equals(
|
||||
'BoxConstraints has NaN values in minWidth, maxWidth, and maxHeight.\n'
|
||||
'BoxConstraints(NaN<=w<=NaN, 2.0<=h<=NaN; NOT NORMALIZED)'
|
||||
'The offending constraints were:\n'
|
||||
' BoxConstraints(NaN<=w<=NaN, 2.0<=h<=NaN; NOT NORMALIZED)'
|
||||
));
|
||||
|
||||
result = 'no exception';
|
||||
@ -55,7 +56,8 @@ void main() {
|
||||
}
|
||||
expect(result, equals(
|
||||
'BoxConstraints has a NaN value in minHeight.\n'
|
||||
'BoxConstraints(0.0<=w<=Infinity, NaN<=h<=Infinity; NOT NORMALIZED)'
|
||||
'The offending constraints were:\n'
|
||||
' BoxConstraints(0.0<=w<=Infinity, NaN<=h<=Infinity; NOT NORMALIZED)'
|
||||
));
|
||||
|
||||
result = 'no exception';
|
||||
@ -67,7 +69,8 @@ void main() {
|
||||
}
|
||||
expect(result, equals(
|
||||
'BoxConstraints has NaN values in maxWidth and minHeight.\n'
|
||||
'BoxConstraints(0.0<=w<=NaN, NaN<=h<=Infinity; NOT NORMALIZED)'
|
||||
'The offending constraints were:\n'
|
||||
' BoxConstraints(0.0<=w<=NaN, NaN<=h<=Infinity; NOT NORMALIZED)'
|
||||
));
|
||||
});
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user