Make ArgumentError.check* functions return the valid argument.

This makes these checks useful in situations where you don't want to spend
an extra statement, like `=>` bodies or initializer lists (including
forwarding generative constructors).

Change-Id: Ia55b8741a7c75af631db48ac70e64597d8f96c73
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/135649
Commit-Queue: Lasse R.H. Nielsen <lrn@google.com>
Reviewed-by: Lasse R.H. Nielsen <lrn@google.com>
Reviewed-by: Bob Nystrom <rnystrom@google.com>
This commit is contained in:
Lasse R.H. Nielsen 2020-03-23 18:00:09 +00:00 committed by commit-bot@chromium.org
parent 1181f44378
commit 8ab3dcf709
7 changed files with 119 additions and 16 deletions

View file

@ -23,6 +23,11 @@
which was added to `AssertionError` when the second operand to `assert`
was allowed. The value of that getter on a `TypeError` was the same
string as returned by `toString`, so it is still available.
* `ArgumentError.checkNotNull` and the `RangeError` static methods
`checkValueInInterval`, `checkValidIndex` and `checkNotNegative`
all return their first argument on success.
This makes these functions more convenient to use in-line in,
for example, `=>` function bodies or constructor initialization lists.
#### `dart:developer`

View file

@ -423,7 +423,7 @@ library from "org-dartlang-test:///main.dart" as main {
return 1.{dart.core::int::unary-}();
}
method /* from org-dartlang-sdk:///sdk/lib/collection/list.dart */ insert(dart.core::int* index, generic-covariant-impl dart.core::int* element) → void {
dart.core::ArgumentError::checkNotNull(index, "index");
dart.core::ArgumentError::checkNotNull<dart.core::int*>(index, "index");
dart.core::RangeError::checkValueInInterval(index, 0, this.{dart.core::List::length}, "index");
if(index.{dart.core::num::==}(this.{dart.core::List::length})) {
this.{dart.collection::ListMixin::add}(element);

View file

@ -423,7 +423,7 @@ library from "org-dartlang-test:///main.dart" as main {
return 1.{dart.core::int::unary-}();
}
method /* from org-dartlang-sdk:///sdk/lib/collection/list.dart */ insert(dart.core::int* index, generic-covariant-impl dart.core::int* element) → void {
dart.core::ArgumentError::checkNotNull(index, "index");
dart.core::ArgumentError::checkNotNull<dart.core::int*>(index, "index");
dart.core::RangeError::checkValueInInterval(index, 0, this.{dart.core::List::length}, "index");
if(index.{dart.core::num::==}(this.{dart.core::List::length})) {
this.{dart.collection::ListMixin::add}(element);

View file

@ -184,10 +184,16 @@ class ArgumentError extends Error {
/**
* Throws if [argument] is `null`.
*
* If [name] is supplied, it is used as the parameter name
* in the error message.
*
* Returns the [argument] if it is not null.
*/
@Since("2.1")
static void checkNotNull(Object argument, [String name]) {
static T checkNotNull<@Since("2.8") T>(T argument, [String name]) {
if (argument == null) throw ArgumentError.notNull(name);
return argument;
}
// Helper functions for toString overridden in subclasses.
@ -279,30 +285,42 @@ class RangeError extends ArgumentError {
[String name, String message, int length]) = IndexError;
/**
* Check that a [value] lies in a specific interval.
* Check that an integer [value] lies in a specific interval.
*
* Throws if [value] is not in the interval.
* The interval is from [minValue] to [maxValue], both inclusive.
*
* If [name] or [message] are provided, they are used as the parameter
* name and message text of the thrown error.
*
* Returns [value] if it is in the interval.
*/
static void checkValueInInterval(int value, int minValue, int maxValue,
static int checkValueInInterval(int value, int minValue, int maxValue,
[String name, String message]) {
if (value < minValue || value > maxValue) {
throw RangeError.range(value, minValue, maxValue, name, message);
}
return value;
}
/**
* Check that a value is a valid index into an indexable object.
* Check that [index] is a valid index into an indexable object.
*
* Throws if [index] is not a valid index into [indexable].
*
* An indexable object is one that has a `length` and a and index-operator
* `[]` that accepts an index if `0 <= index < length`.
*
* If [name] or [message] are provided, they are used as the parameter
* name and message text of the thrown error. If [name] is omitted, it
* defaults to `"index"`.
*
* If [length] is provided, it is used as the length of the indexable object,
* otherwise the length is found as `indexable.length`.
*
* Returns [index] if it is a valid index.
*/
static void checkValidIndex(int index, dynamic indexable,
static int checkValidIndex(int index, dynamic indexable,
[String name, int length, String message]) {
length ??= indexable.length;
// Comparing with `0` as receiver produces better dart2js type inference.
@ -310,6 +328,7 @@ class RangeError extends ArgumentError {
name ??= "index";
throw RangeError.index(index, indexable, name, message, length);
}
return index;
}
/**
@ -347,12 +366,19 @@ class RangeError extends ArgumentError {
}
/**
* Check that an integer value isn't negative.
* Check that an integer value is non-negative.
*
* Throws if the value is negative.
*
* If [name] or [message] are provided, they are used as the parameter
* name and message text of the thrown error. If [name] is omitted, it
* defaults to `index`.
*
* Returns [value] if it is not negative.
*/
static void checkNotNegative(int value, [String name, String message]) {
static int checkNotNegative(int value, [String name, String message]) {
if (value < 0) throw RangeError.range(value, 0, null, name, message);
return value;
}
String get _errorName => "RangeError";

View file

@ -182,10 +182,16 @@ class ArgumentError extends Error {
/**
* Throws if [argument] is `null`.
*
* If [name] is supplied, it is used as the parameter name
* in the error message.
*
* Returns the [argument] if it is not null.
*/
@Since("2.1")
static void checkNotNull(Object? argument, [String? name]) {
static T checkNotNull<@Since("2.8") T>(T? argument, [String? name]) {
if (argument == null) throw ArgumentError.notNull(name);
return argument;
}
// Helper functions for toString overridden in subclasses.
@ -274,30 +280,42 @@ class RangeError extends ArgumentError {
[String? name, String? message, int? length]) = IndexError;
/**
* Check that a [value] lies in a specific interval.
* Check that an integer [value] lies in a specific interval.
*
* Throws if [value] is not in the interval.
* The interval is from [minValue] to [maxValue], both inclusive.
*
* If [name] or [message] are provided, they are used as the parameter
* name and message text of the thrown error.
*
* Returns [value] if it is in the interval.
*/
static void checkValueInInterval(int value, int minValue, int maxValue,
static int checkValueInInterval(int value, int minValue, int maxValue,
[String? name, String? message]) {
if (value < minValue || value > maxValue) {
throw RangeError.range(value, minValue, maxValue, name, message);
}
return value;
}
/**
* Check that a value is a valid index into an indexable object.
* Check that [index] is a valid index into an indexable object.
*
* Throws if [index] is not a valid index into [indexable].
*
* An indexable object is one that has a `length` and a and index-operator
* `[]` that accepts an index if `0 <= index < length`.
*
* If [name] or [message] are provided, they are used as the parameter
* name and message text of the thrown error. If [name] is omitted, it
* defaults to `"index"`.
*
* If [length] is provided, it is used as the length of the indexable object,
* otherwise the length is found as `indexable.length`.
*
* Returns [index] if it is a valid index.
*/
static void checkValidIndex(int index, dynamic indexable,
static int checkValidIndex(int index, dynamic indexable,
[String? name, int? length, String? message]) {
length ??= (indexable.length as int);
// Comparing with `0` as receiver produces better dart2js type inference.
@ -305,6 +323,7 @@ class RangeError extends ArgumentError {
name ??= "index";
throw RangeError.index(index, indexable, name, message, length);
}
return index;
}
/**
@ -342,12 +361,19 @@ class RangeError extends ArgumentError {
}
/**
* Check that an integer value isn't negative.
* Check that an integer value is non-negative.
*
* Throws if the value is negative.
*
* If [name] or [message] are provided, they are used as the parameter
* name and message text of the thrown error. If [name] is omitted, it
* defaults to `index`.
*
* Returns [value] if it is not negative.
*/
static void checkNotNegative(int value, [String? name, String? message]) {
static int checkNotNegative(int value, [String? name, String? message]) {
if (value < 0) throw RangeError.range(value, 0, null, name, message);
return value;
}
String get _errorName => "RangeError";

View file

@ -74,4 +74,27 @@ main() {
"RangeError: Index out of range: "
"index must not be negative: -5",
new RangeError.index(-5, [1, 2, 3]).toString());
Expect.equals(42, ArgumentError.checkNotNull(42));
Expect.equals(42, ArgumentError.checkNotNull(42, "name"));
Expect.throwsArgumentError(() => ArgumentError.checkNotNull(null));
Expect.equals(1, RangeError.checkNotNegative(1));
Expect.equals(0, RangeError.checkNotNegative(0));
Expect.throwsRangeError(() => RangeError.checkNotNegative(-1));
Expect.equals(1, RangeError.checkValueInInterval(1, 0, 2));
Expect.equals(1, RangeError.checkValueInInterval(1, 1, 2));
Expect.equals(1, RangeError.checkValueInInterval(1, 0, 1));
Expect.equals(1, RangeError.checkValueInInterval(1, 1, 1));
Expect.throwsRangeError(() => RangeError.checkValueInInterval(1, 2, 3));
Expect.throwsRangeError(() => RangeError.checkValueInInterval(1, 1, 0));
Expect.throwsRangeError(() => RangeError.checkValueInInterval(0, 1, 0));
Expect.equals(1, RangeError.checkValidIndex(1, [1, 2]));
Expect.equals(1, RangeError.checkValidIndex(1, null, null, 2));
Expect.throwsRangeError(() => RangeError.checkValidIndex(1, []));
Expect.throwsRangeError(() => RangeError.checkValidIndex(1, null, null, 1));
Expect.throwsRangeError(() => RangeError.checkValidIndex(-1, [1, 2, 3]));
Expect.throwsRangeError(() => RangeError.checkValidIndex(-1, null, null, 3));
}

View file

@ -74,4 +74,27 @@ main() {
"RangeError: Index out of range: "
"index must not be negative: -5",
new RangeError.index(-5, [1, 2, 3]).toString());
Expect.equals(42, ArgumentError.checkNotNull(42));
Expect.equals(42, ArgumentError.checkNotNull(42, "name"));
Expect.throwsArgumentError(() => ArgumentError.checkNotNull(null));
Expect.equals(1, RangeError.checkNotNegative(1));
Expect.equals(0, RangeError.checkNotNegative(0));
Expect.throwsRangeError(() => RangeError.checkNotNegative(-1));
Expect.equals(1, RangeError.checkValueInInterval(1, 0, 2));
Expect.equals(1, RangeError.checkValueInInterval(1, 1, 2));
Expect.equals(1, RangeError.checkValueInInterval(1, 0, 1));
Expect.equals(1, RangeError.checkValueInInterval(1, 1, 1));
Expect.throwsRangeError(() => RangeError.checkValueInInterval(1, 2, 3));
Expect.throwsRangeError(() => RangeError.checkValueInInterval(1, 1, 0));
Expect.throwsRangeError(() => RangeError.checkValueInInterval(0, 1, 0));
Expect.equals(1, RangeError.checkValidIndex(1, [1, 2]));
Expect.equals(1, RangeError.checkValidIndex(1, null, null, 2));
Expect.throwsRangeError(() => RangeError.checkValidIndex(1, []));
Expect.throwsRangeError(() => RangeError.checkValidIndex(1, null, null, 1));
Expect.throwsRangeError(() => RangeError.checkValidIndex(-1, [1, 2, 3]));
Expect.throwsRangeError(() => RangeError.checkValidIndex(-1, null, null, 3));
}