From 8ab3dcf709c5fbaa8d8bc5ec1fbe0c58f48edcc7 Mon Sep 17 00:00:00 2001 From: "Lasse R.H. Nielsen" Date: Mon, 23 Mar 2020 18:00:09 +0000 Subject: [PATCH] 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 Reviewed-by: Lasse R.H. Nielsen Reviewed-by: Bob Nystrom --- CHANGELOG.md | 5 +++ .../no_outline_change_22.yaml.world.1.expect | 2 +- .../no_outline_change_22.yaml.world.2.expect | 2 +- sdk/lib/core/errors.dart | 40 +++++++++++++++---- sdk_nnbd/lib/core/errors.dart | 40 +++++++++++++++---- tests/corelib/errors_test.dart | 23 +++++++++++ tests/corelib_2/errors_test.dart | 23 +++++++++++ 7 files changed, 119 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f28251a9830..fbf5e770035 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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` diff --git a/pkg/front_end/testcases/incremental_initialize_from_dill/no_outline_change_22.yaml.world.1.expect b/pkg/front_end/testcases/incremental_initialize_from_dill/no_outline_change_22.yaml.world.1.expect index a20d4ff5815..4a9ec120770 100644 --- a/pkg/front_end/testcases/incremental_initialize_from_dill/no_outline_change_22.yaml.world.1.expect +++ b/pkg/front_end/testcases/incremental_initialize_from_dill/no_outline_change_22.yaml.world.1.expect @@ -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(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); diff --git a/pkg/front_end/testcases/incremental_initialize_from_dill/no_outline_change_22.yaml.world.2.expect b/pkg/front_end/testcases/incremental_initialize_from_dill/no_outline_change_22.yaml.world.2.expect index a20d4ff5815..4a9ec120770 100644 --- a/pkg/front_end/testcases/incremental_initialize_from_dill/no_outline_change_22.yaml.world.2.expect +++ b/pkg/front_end/testcases/incremental_initialize_from_dill/no_outline_change_22.yaml.world.2.expect @@ -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(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); diff --git a/sdk/lib/core/errors.dart b/sdk/lib/core/errors.dart index bb124317883..e1a2672b094 100644 --- a/sdk/lib/core/errors.dart +++ b/sdk/lib/core/errors.dart @@ -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"; diff --git a/sdk_nnbd/lib/core/errors.dart b/sdk_nnbd/lib/core/errors.dart index 8a87c8b9526..005cb8402b4 100644 --- a/sdk_nnbd/lib/core/errors.dart +++ b/sdk_nnbd/lib/core/errors.dart @@ -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"; diff --git a/tests/corelib/errors_test.dart b/tests/corelib/errors_test.dart index 763b4863e70..9d040c98a16 100644 --- a/tests/corelib/errors_test.dart +++ b/tests/corelib/errors_test.dart @@ -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)); } diff --git a/tests/corelib_2/errors_test.dart b/tests/corelib_2/errors_test.dart index 763b4863e70..9d040c98a16 100644 --- a/tests/corelib_2/errors_test.dart +++ b/tests/corelib_2/errors_test.dart @@ -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)); }