Migrate to null safety (#159)
Initial migration to null safety, futher updates still pending and breaking changes possible.
diff --git a/.travis.yml b/.travis.yml
index a80f7c7..bdf0b3c 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,19 +1,31 @@
language: dart
dart:
-- 2.3.0
- dev
-dart_task:
-- test: -p vm
-- dartanalyzer: --fatal-infos --fatal-warnings .
-
-matrix:
+jobs:
include:
- - dart: dev
- dart_task: dartfmt
+ - stage: analyze_and_format
+ name: "Analyze"
+ dart: dev
+ os: linux
+ script: dartanalyzer --fatal-warnings --fatal-infos .
+ - stage: analyze_and_format
+ name: "Format"
+ dart: dev
+ os: linux
+ script: dartfmt -n --set-exit-if-changed .
+ - stage: test
+ name: "Vm Tests"
+ dart: dev
+ os: linux
+ script: pub run test -p vm
+ - stage: test
+ name: "Web Tests"
+ dart: dev
+ os: linux
+ script: pub run test -p chrome
-# Only building master means that we don't run two builds for each pull request.
branches:
only: [master]
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4f324d0..fcc3229 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,4 +1,4 @@
-## 1.6.1-dev
+## 2.0.0-nullsafety
## 1.6.0
diff --git a/lib/command_runner.dart b/lib/command_runner.dart
index 0fbf697..0a6566f 100644
--- a/lib/command_runner.dart
+++ b/lib/command_runner.dart
@@ -45,7 +45,7 @@
///
/// If a subclass overrides this to return a string, it will automatically be
/// added to the end of [usage].
- String get usageFooter => null;
+ String? get usageFooter => null;
/// Returns [usage] with [description] removed from the beginning.
String get _usageWithoutDescription {
@@ -60,7 +60,7 @@
buffer.write(_wrap(
'Run "$executableName help <command>" for more information about a command.'));
if (usageFooter != null) {
- buffer.write('\n${_wrap(usageFooter)}');
+ buffer.write('\n${_wrap(usageFooter!)}');
}
return buffer.toString();
}
@@ -77,7 +77,7 @@
ArgParser get argParser => _argParser;
final ArgParser _argParser;
- CommandRunner(this.executableName, this.description, {int usageLineLength})
+ CommandRunner(this.executableName, this.description, {int? usageLineLength})
: _argParser = ArgParser(usageLineLength: usageLineLength) {
argParser.addFlag('help',
abbr: 'h', negatable: false, help: 'Print this usage information.');
@@ -91,7 +91,7 @@
void printUsage() => print(usage);
/// Throws a [UsageException] with [message].
- void usageException(String message) =>
+ Never usageException(String message) =>
throw UsageException(message, _usageWithoutDescription);
/// Adds [Command] as a top-level command to this runner.
@@ -108,7 +108,7 @@
///
/// This always returns a [Future] in case the command is asynchronous. The
/// [Future] will throw a [UsageException] if [args] was invalid.
- Future<T> run(Iterable<String> args) =>
+ Future<T?> run(Iterable<String> args) =>
Future.sync(() => runCommand(parse(args)));
/// Parses [args] and returns the result, converting an [ArgParserException]
@@ -124,11 +124,10 @@
var command = commands[error.commands.first];
for (var commandName in error.commands.skip(1)) {
- command = command.subcommands[commandName];
+ command = command!.subcommands[commandName];
}
- command.usageException(error.message);
- return null;
+ command!.usageException(error.message);
}
}
@@ -142,10 +141,10 @@
/// here to enable verbose logging before running the command.
///
/// This returns the return value of [Command.run].
- Future<T> runCommand(ArgResults topLevelResults) async {
+ Future<T?> runCommand(ArgResults topLevelResults) async {
var argResults = topLevelResults;
var commands = _commands;
- Command command;
+ Command? command;
var commandString = executableName;
while (commands.isNotEmpty) {
@@ -170,11 +169,11 @@
}
// Step into the command.
- argResults = argResults.command;
+ argResults = argResults.command!;
command = commands[argResults.name];
- command._globalResults = topLevelResults;
+ command!._globalResults = topLevelResults;
command._argResults = argResults;
- commands = command._subcommands;
+ commands = command._subcommands as Map<String, Command<T>>;
commandString += ' ${argResults.name}';
if (argResults.options.contains('help') && argResults['help']) {
@@ -184,20 +183,20 @@
}
if (topLevelResults['help']) {
- command.printUsage();
+ command!.printUsage();
return null;
}
// Make sure there aren't unexpected arguments.
- if (!command.takesArguments && argResults.rest.isNotEmpty) {
+ if (!command!.takesArguments && argResults.rest.isNotEmpty) {
command.usageException(
'Command "${argResults.name}" does not take any arguments.');
}
- return (await command.run()) as T;
+ return (await command.run()) as T?;
}
- String _wrap(String text, {int hangingIndent}) => wrapText(text,
+ String _wrap(String text, {int? hangingIndent}) => wrapText(text,
length: argParser.usageLineLength, hangingIndent: hangingIndent);
}
@@ -229,7 +228,7 @@
for (var command = parent; command != null; command = command.parent) {
parents.add(command.name);
}
- parents.add(runner.executableName);
+ parents.add(runner!.executableName);
var invocation = parents.reversed.join(' ');
return _subcommands.isNotEmpty
@@ -241,31 +240,31 @@
///
/// This will be `null` until [addSubcommand] has been called with
/// this command.
- Command<T> get parent => _parent;
- Command<T> _parent;
+ Command<T>? get parent => _parent;
+ Command<T>? _parent;
/// The command runner for this command.
///
/// This will be `null` until [CommandRunner.addCommand] has been called with
/// this command or one of its parents.
- CommandRunner<T> get runner {
+ CommandRunner<T>? get runner {
if (parent == null) return _runner;
- return parent.runner;
+ return parent!.runner;
}
- CommandRunner<T> _runner;
+ CommandRunner<T>? _runner;
/// The parsed global argument results.
///
/// This will be `null` until just before [Command.run] is called.
- ArgResults get globalResults => _globalResults;
- ArgResults _globalResults;
+ ArgResults? get globalResults => _globalResults;
+ ArgResults? _globalResults;
/// The parsed argument results for this command.
///
/// This will be `null` until just before [Command.run] is called.
- ArgResults get argResults => _argResults;
- ArgResults _argResults;
+ ArgResults? get argResults => _argResults;
+ ArgResults? _argResults;
/// The argument parser for this command.
///
@@ -289,9 +288,9 @@
///
/// If a subclass overrides this to return a string, it will automatically be
/// added to the end of [usage].
- String get usageFooter => null;
+ String? get usageFooter => null;
- String _wrap(String text, {int hangingIndent}) {
+ String _wrap(String text, {int? hangingIndent}) {
return wrapText(text,
length: argParser.usageLineLength, hangingIndent: hangingIndent);
}
@@ -316,11 +315,11 @@
buffer.writeln();
buffer.write(
- _wrap('Run "${runner.executableName} help" to see global options.'));
+ _wrap('Run "${runner!.executableName} help" to see global options.'));
if (usageFooter != null) {
buffer.writeln();
- buffer.write(_wrap(usageFooter));
+ buffer.write(_wrap(usageFooter!));
}
return buffer.toString();
@@ -374,7 +373,7 @@
///
/// The return value is wrapped in a `Future` if necessary and returned by
/// [CommandRunner.runCommand].
- FutureOr<T> run() {
+ FutureOr<T>? run() {
throw UnimplementedError(_wrap('Leaf command $this must implement run().'));
}
@@ -395,7 +394,7 @@
void printUsage() => print(usage);
/// Throws a [UsageException] with [message].
- void usageException(String message) =>
+ Never usageException(String message) =>
throw UsageException(_wrap(message), _usageWithoutDescription);
}
@@ -404,13 +403,13 @@
/// [isSubcommand] indicates whether the commands should be called "commands" or
/// "subcommands".
String _getCommandUsage(Map<String, Command> commands,
- {bool isSubcommand = false, int lineLength}) {
+ {bool isSubcommand = false, int? lineLength}) {
// Don't include aliases.
var names =
- commands.keys.where((name) => !commands[name].aliases.contains(name));
+ commands.keys.where((name) => !commands[name]!.aliases.contains(name));
// Filter out hidden ones, unless they are all hidden.
- var visible = names.where((name) => !commands[name].hidden);
+ var visible = names.where((name) => !commands[name]!.hidden);
if (visible.isNotEmpty) names = visible;
// Show the commands alphabetically.
@@ -420,7 +419,7 @@
var buffer = StringBuffer('Available ${isSubcommand ? "sub" : ""}commands:');
var columnStart = length + 5;
for (var name in names) {
- var lines = wrapTextAsLines(commands[name].summary,
+ var lines = wrapTextAsLines(commands[name]!.summary,
start: columnStart, length: lineLength);
buffer.writeln();
buffer.write(' ${padRight(name, length)} ${lines.first}');
diff --git a/lib/src/allow_anything_parser.dart b/lib/src/allow_anything_parser.dart
index e6f13d0..16f97fd 100644
--- a/lib/src/allow_anything_parser.dart
+++ b/lib/src/allow_anything_parser.dart
@@ -20,21 +20,21 @@
@override
bool get allowsAnything => true;
@override
- int get usageLineLength => null;
+ int? get usageLineLength => null;
@override
- ArgParser addCommand(String name, [ArgParser parser]) {
+ ArgParser addCommand(String name, [ArgParser? parser]) {
throw UnsupportedError(
"ArgParser.allowAnything().addCommands() isn't supported.");
}
@override
void addFlag(String name,
- {String abbr,
- String help,
- bool defaultsTo = false,
+ {String? abbr,
+ String? help,
+ bool? defaultsTo = false,
bool negatable = true,
- void Function(bool) callback,
+ void Function(bool)? callback,
bool hide = false}) {
throw UnsupportedError(
"ArgParser.allowAnything().addFlag() isn't supported.");
@@ -42,15 +42,15 @@
@override
void addOption(String name,
- {String abbr,
- String help,
- String valueHelp,
- Iterable<String> allowed,
- Map<String, String> allowedHelp,
- String defaultsTo,
- Function callback,
+ {String? abbr,
+ String? help,
+ String? valueHelp,
+ Iterable<String>? allowed,
+ Map<String, String>? allowedHelp,
+ String? defaultsTo,
+ Function? callback,
bool allowMultiple = false,
- bool splitCommas,
+ bool? splitCommas,
bool hide = false}) {
throw UnsupportedError(
"ArgParser.allowAnything().addOption() isn't supported.");
@@ -58,13 +58,13 @@
@override
void addMultiOption(String name,
- {String abbr,
- String help,
- String valueHelp,
- Iterable<String> allowed,
- Map<String, String> allowedHelp,
- Iterable<String> defaultsTo,
- void Function(List<String>) callback,
+ {String? abbr,
+ String? help,
+ String? valueHelp,
+ Iterable<String>? allowed,
+ Map<String, String>? allowedHelp,
+ Iterable<String>? defaultsTo,
+ void Function(List<String>)? callback,
bool splitCommas = true,
bool hide = false}) {
throw UnsupportedError(
@@ -93,5 +93,5 @@
}
@override
- Option findByAbbreviation(String abbr) => null;
+ Option? findByAbbreviation(String abbr) => null;
}
diff --git a/lib/src/arg_parser.dart b/lib/src/arg_parser.dart
index aae89d6..7ccb5b4 100644
--- a/lib/src/arg_parser.dart
+++ b/lib/src/arg_parser.dart
@@ -44,7 +44,7 @@
/// there is no whitespace at which to split).
///
/// If null (the default), help messages are not wrapped.
- final int usageLineLength;
+ final int? usageLineLength;
/// Whether or not this parser treats unrecognized options as non-option
/// arguments.
@@ -56,7 +56,7 @@
/// flags and options that appear after positional arguments. If it's `false`,
/// the parser stops parsing as soon as it finds an argument that is neither
/// an option nor a command.
- factory ArgParser({bool allowTrailingOptions = true, int usageLineLength}) =>
+ factory ArgParser({bool allowTrailingOptions = true, int? usageLineLength}) =>
ArgParser._(<String, Option>{}, <String, ArgParser>{},
allowTrailingOptions: allowTrailingOptions,
usageLineLength: usageLineLength);
@@ -75,7 +75,7 @@
options = UnmodifiableMapView(options),
_commands = commands,
commands = UnmodifiableMapView(commands),
- allowTrailingOptions = allowTrailingOptions ?? false;
+ allowTrailingOptions = allowTrailingOptions;
/// Defines a command.
///
@@ -85,7 +85,7 @@
///
/// Note that adding commands this way will not impact the [usage] string. To
/// add commands which are included in the usage string see `CommandRunner`.
- ArgParser addCommand(String name, [ArgParser parser]) {
+ ArgParser addCommand(String name, [ArgParser? parser]) {
// Make sure the name isn't in use.
if (_commands.containsKey(name)) {
throw ArgumentError('Duplicate command "$name".');
@@ -125,11 +125,11 @@
/// * There is already an option named [name].
/// * There is already an option using abbreviation [abbr].
void addFlag(String name,
- {String abbr,
- String help,
- bool defaultsTo = false,
+ {String? abbr,
+ String? help,
+ bool? defaultsTo = false,
bool negatable = true,
- void Function(bool) callback,
+ void Function(bool)? callback,
bool hide = false}) {
_addOption(
name,
@@ -186,15 +186,15 @@
/// * There is already an option using abbreviation [abbr].
/// * [splitCommas] is passed but [allowMultiple] is `false`.
void addOption(String name,
- {String abbr,
- String help,
- String valueHelp,
- Iterable<String> allowed,
- Map<String, String> allowedHelp,
- String defaultsTo,
- Function callback,
+ {String? abbr,
+ String? help,
+ String? valueHelp,
+ Iterable<String>? allowed,
+ Map<String, String>? allowedHelp,
+ String? defaultsTo,
+ Function? callback,
@Deprecated('Use addMultiOption() instead.') bool allowMultiple = false,
- @Deprecated('Use addMultiOption() instead.') bool splitCommas,
+ @Deprecated('Use addMultiOption() instead.') bool? splitCommas,
bool hide = false}) {
if (!allowMultiple && splitCommas != null) {
throw ArgumentError(
@@ -255,13 +255,13 @@
/// * There is already an option with name [name].
/// * There is already an option using abbreviation [abbr].
void addMultiOption(String name,
- {String abbr,
- String help,
- String valueHelp,
- Iterable<String> allowed,
- Map<String, String> allowedHelp,
- Iterable<String> defaultsTo,
- void Function(List<String>) callback,
+ {String? abbr,
+ String? help,
+ String? valueHelp,
+ Iterable<String>? allowed,
+ Map<String, String>? allowedHelp,
+ Iterable<String>? defaultsTo,
+ void Function(List<String>)? callback,
bool splitCommas = true,
bool hide = false}) {
_addOption(
@@ -280,16 +280,16 @@
void _addOption(
String name,
- String abbr,
- String help,
- String valueHelp,
- Iterable<String> allowed,
- Map<String, String> allowedHelp,
+ String? abbr,
+ String? help,
+ String? valueHelp,
+ Iterable<String>? allowed,
+ Map<String, String>? allowedHelp,
defaultsTo,
- Function callback,
+ Function? callback,
OptionType type,
{bool negatable = false,
- bool splitCommas,
+ bool? splitCommas,
bool hide = false}) {
// Make sure the name isn't in use.
if (_options.containsKey(name)) {
@@ -341,16 +341,19 @@
/// Get the default value for an option. Useful after parsing to test if the
/// user specified something other than the default.
dynamic getDefault(String option) {
- if (!options.containsKey(option)) {
+ var value = options[option];
+ if (value == null) {
throw ArgumentError('No option named $option');
}
- return options[option].defaultsTo;
+ return value.defaultsTo;
}
/// Finds the option whose abbreviation is [abbr], or `null` if no option has
/// that abbreviation.
- Option findByAbbreviation(String abbr) {
- return options.values
- .firstWhere((option) => option.abbr == abbr, orElse: () => null);
+ Option? findByAbbreviation(String abbr) {
+ for (var option in options.values) {
+ if (option.abbr == abbr) return option;
+ }
+ return null;
}
}
diff --git a/lib/src/arg_parser_exception.dart b/lib/src/arg_parser_exception.dart
index f0d57d5..56ac851 100644
--- a/lib/src/arg_parser_exception.dart
+++ b/lib/src/arg_parser_exception.dart
@@ -9,7 +9,7 @@
/// This will be empty if the error was on the root parser.
final List<String> commands;
- ArgParserException(String message, [Iterable<String> commands])
+ ArgParserException(String message, [Iterable<String>? commands])
: commands = commands == null ? const [] : List.unmodifiable(commands),
super(message);
}
diff --git a/lib/src/arg_results.dart b/lib/src/arg_results.dart
index 6345076..fb2d0c8 100644
--- a/lib/src/arg_results.dart
+++ b/lib/src/arg_results.dart
@@ -13,8 +13,8 @@
ArgResults newArgResults(
ArgParser parser,
Map<String, dynamic> parsed,
- String name,
- ArgResults command,
+ String? name,
+ ArgResults? command,
List<String> rest,
List<String> arguments) {
return ArgResults._(parser, parsed, name, command, rest, arguments);
@@ -34,12 +34,12 @@
/// The name of the command for which these options are parsed, or `null` if
/// these are the top-level results.
- final String name;
+ final String? name;
/// The command that was selected, or `null` if none was.
///
/// This will contain the options that were selected for that command.
- final ArgResults command;
+ final ArgResults? command;
/// The remaining command-line arguments that were not parsed as options or
/// flags.
@@ -65,7 +65,7 @@
throw ArgumentError('Could not find an option named "$name".');
}
- return _parser.options[name].getOrDefault(_parsed[name]);
+ return _parser.options[name]!.getOrDefault(_parsed[name]);
}
/// The names of the available options.
diff --git a/lib/src/help_command.dart b/lib/src/help_command.dart
index 255f2d4..c3c2bbf 100644
--- a/lib/src/help_command.dart
+++ b/lib/src/help_command.dart
@@ -13,37 +13,37 @@
@override
String get description =>
- 'Display help information for ${runner.executableName}.';
+ 'Display help information for ${runner!.executableName}.';
@override
- String get invocation => '${runner.executableName} help [command]';
+ String get invocation => '${runner!.executableName} help [command]';
@override
bool get hidden => true;
@override
- T run() {
+ T? run() {
// Show the default help if no command was specified.
- if (argResults.rest.isEmpty) {
- runner.printUsage();
+ if (argResults!.rest.isEmpty) {
+ runner!.printUsage();
return null;
}
// Walk the command tree to show help for the selected command or
// subcommand.
- var commands = runner.commands;
- Command command;
- var commandString = runner.executableName;
+ var commands = runner!.commands;
+ Command? command;
+ var commandString = runner!.executableName;
- for (var name in argResults.rest) {
+ for (var name in argResults!.rest) {
if (commands.isEmpty) {
- command.usageException(
+ command!.usageException(
'Command "$commandString" does not expect a subcommand.');
}
if (commands[name] == null) {
if (command == null) {
- runner.usageException('Could not find a command named "$name".');
+ runner!.usageException('Could not find a command named "$name".');
}
command.usageException(
@@ -51,11 +51,11 @@
}
command = commands[name];
- commands = command.subcommands;
+ commands = command!.subcommands as Map<String, Command<T>>;
commandString += ' $name';
}
- command.printUsage();
+ command!.printUsage();
return null;
}
}
diff --git a/lib/src/option.dart b/lib/src/option.dart
index 7fd9160..958929a 100644
--- a/lib/src/option.dart
+++ b/lib/src/option.dart
@@ -8,16 +8,16 @@
/// get to it. This function isn't exported to the public API of the package.
Option newOption(
String name,
- String abbr,
- String help,
- String valueHelp,
- Iterable<String> allowed,
- Map<String, String> allowedHelp,
+ String? abbr,
+ String? help,
+ String? valueHelp,
+ Iterable<String>? allowed,
+ Map<String, String>? allowedHelp,
defaultsTo,
- Function callback,
+ Function? callback,
OptionType type,
- {bool negatable,
- bool splitCommas,
+ {bool? negatable,
+ bool? splitCommas,
bool hide = false}) {
return Option._(name, abbr, help, valueHelp, allowed, allowedHelp, defaultsTo,
callback, type,
@@ -35,22 +35,22 @@
///
/// For example, `abbr: "a"` will allow the user to pass `-a value` or
/// `-avalue`.
- final String abbr;
+ final String? abbr;
@Deprecated('Use abbr instead.')
- String get abbreviation => abbr;
+ String? get abbreviation => abbr;
/// A description of this option.
- final String help;
+ final String? help;
/// A name for the value this option takes.
- final String valueHelp;
+ final String? valueHelp;
/// A list of valid values for this option.
- final List<String> allowed;
+ final List<String>? allowed;
/// A map from values in [allowed] to documentation for those values.
- final Map<String, String> allowedHelp;
+ final Map<String, String>? allowedHelp;
/// The value this option will have if the user doesn't explicitly pass it.
final dynamic defaultsTo;
@@ -64,10 +64,10 @@
/// value to `false`.
///
/// This is `null` unless [type] is [OptionType.flag].
- final bool negatable;
+ final bool? negatable;
/// The callback to invoke with the option's value when the option is parsed.
- final Function callback;
+ final Function? callback;
/// Whether this is a flag, a single value option, or a multi-value option.
final OptionType type;
@@ -93,13 +93,13 @@
this.abbr,
this.help,
this.valueHelp,
- Iterable<String> allowed,
- Map<String, String> allowedHelp,
+ Iterable<String>? allowed,
+ Map<String, String>? allowedHelp,
this.defaultsTo,
this.callback,
OptionType type,
{this.negatable,
- bool splitCommas,
+ bool? splitCommas,
this.hide = false})
: allowed = allowed == null ? null : List.unmodifiable(allowed),
allowedHelp =
@@ -119,6 +119,7 @@
throw ArgumentError('Name "$name" contains invalid characters.');
}
+ var abbr = this.abbr;
if (abbr != null) {
if (abbr.length != 1) {
throw ArgumentError('Abbreviation must be null or have length 1.');
diff --git a/lib/src/parser.dart b/lib/src/parser.dart
index cb15288..4860a2e 100644
--- a/lib/src/parser.dart
+++ b/lib/src/parser.dart
@@ -16,11 +16,11 @@
class Parser {
/// If parser is parsing a command's options, this will be the name of the
/// command. For top-level results, this returns `null`.
- final String commandName;
+ final String? commandName;
/// The parser for the supercommand of this command parser, or `null` if this
/// is the top-level parser.
- final Parser parent;
+ final Parser? parent;
/// The grammar being parsed.
final ArgParser grammar;
@@ -35,7 +35,7 @@
final Map<String, dynamic> results = <String, dynamic>{};
Parser(this.commandName, this.grammar, this.args,
- [this.parent, List<String> rest]) {
+ [this.parent, List<String>? rest]) {
if (rest != null) this.rest.addAll(rest);
}
@@ -50,7 +50,7 @@
grammar, const {}, commandName, null, arguments, arguments);
}
- ArgResults commandResults;
+ ArgResults? commandResults;
// Parse the args.
while (args.isNotEmpty) {
@@ -71,7 +71,6 @@
try {
commandResults = commandParser.parse();
} on ArgParserException catch (error) {
- if (commandName == null) rethrow;
throw ArgParserException(
error.message, [commandName, ...error.commands]);
}
@@ -95,8 +94,8 @@
// Invoke the callbacks.
grammar.options.forEach((name, option) {
- if (option.callback == null) return;
- option.callback(option.getOrDefault(results[name]));
+ var callback = option.callback;
+ if (callback != null) callback(option.getOrDefault(results[name]));
});
// Add in the leftover arguments we didn't parse to the innermost command.
@@ -134,7 +133,7 @@
if (option == null) {
// Walk up to the parent command if possible.
validate(parent != null, 'Could not find an option or flag "-$opt".');
- return parent.parseSoloOption();
+ return parent!.parseSoloOption();
}
args.removeFirst();
@@ -179,7 +178,7 @@
// Walk up to the parent command if possible.
validate(
parent != null, 'Could not find an option with short name "-$c".');
- return parent.parseAbbreviation(innermostCommand);
+ return parent!.parseAbbreviation(innermostCommand);
} else if (!first.isFlag) {
// The first character is a non-flag option, so the rest must be the
// value.
@@ -213,7 +212,7 @@
// Walk up to the parent command if possible.
validate(
parent != null, 'Could not find an option with short name "-$c".');
- parent.parseShortFlag(c);
+ parent!.parseShortFlag(c);
return;
}
@@ -266,18 +265,18 @@
if (option == null) {
// Walk up to the parent command if possible.
validate(parent != null, 'Could not find an option named "$name".');
- return parent.parseLongOption();
+ return parent!.parseLongOption();
}
args.removeFirst();
validate(option.isFlag, 'Cannot negate non-flag option "$name".');
- validate(option.negatable, 'Cannot negate option "$name".');
+ validate(option.negatable!, 'Cannot negate option "$name".');
setFlag(results, option, false);
} else {
// Walk up to the parent command if possible.
validate(parent != null, 'Could not find an option named "$name".');
- return parent.parseLongOption();
+ return parent!.parseLongOption();
}
return true;
@@ -325,7 +324,7 @@
void _validateAllowed(Option option, String value) {
if (option.allowed == null) return;
- validate(option.allowed.contains(value),
+ validate(option.allowed!.contains(value),
'"$value" is not an allowed value for option "${option.name}".');
}
}
diff --git a/lib/src/usage.dart b/lib/src/usage.dart
index d244cad..07bf6ed 100644
--- a/lib/src/usage.dart
+++ b/lib/src/usage.dart
@@ -26,7 +26,7 @@
final List optionsAndSeparators;
/// The working buffer for the generated usage text.
- StringBuffer buffer;
+ late StringBuffer buffer;
/// The column that the "cursor" is currently on.
///
@@ -35,7 +35,7 @@
int currentColumn = 0;
/// The width in characters of each column.
- List<int> columnWidths;
+ late List<int> columnWidths;
/// The number of sequential lines of text that have been written to the last
/// column (which shows help info).
@@ -56,7 +56,7 @@
/// The horizontal character position at which help text is wrapped. Help that
/// extends past this column will be wrapped at the nearest whitespace (or
/// truncated if there is no available whitespace).
- final int lineLength;
+ final int? lineLength;
Usage(this.optionsAndSeparators, {this.lineLength});
@@ -82,15 +82,15 @@
write(0, getAbbreviation(option));
write(1, getLongOption(option));
- if (option.help != null) write(2, option.help);
+ if (option.help != null) write(2, option.help!);
if (option.allowedHelp != null) {
- var allowedNames = option.allowedHelp.keys.toList(growable: false);
+ var allowedNames = option.allowedHelp!.keys.toList(growable: false);
allowedNames.sort();
newline();
for (var name in allowedNames) {
write(1, getAllowedTitle(option, name));
- write(2, option.allowedHelp[name]);
+ write(2, option.allowedHelp![name]!);
}
newline();
} else if (option.allowed != null) {
@@ -122,7 +122,7 @@
String getLongOption(Option option) {
var result;
- if (option.negatable) {
+ if (option.negatable!) {
result = '--[no-]${option.name}';
} else {
result = '--${option.name}';
@@ -155,7 +155,7 @@
// Make room for the allowed help.
if (option.allowedHelp != null) {
- for (var allowed in option.allowedHelp.keys) {
+ for (var allowed in option.allowedHelp!.keys) {
title = math.max(title, getAllowedTitle(option, allowed).length);
}
}
@@ -252,7 +252,7 @@
var allowedBuffer = StringBuffer();
allowedBuffer.write('[');
var first = true;
- for (var allowed in option.allowed) {
+ for (var allowed in option.allowed!) {
if (!first) allowedBuffer.write(', ');
allowedBuffer.write(allowed);
if (isDefault(allowed)) {
diff --git a/lib/src/utils.dart b/lib/src/utils.dart
index 09319ed..4516848 100644
--- a/lib/src/utils.dart
+++ b/lib/src/utils.dart
@@ -32,7 +32,7 @@
///
/// If [length] is not specified, then no wrapping occurs, and the original
/// [text] is returned unchanged.
-String wrapText(String text, {int length, int hangingIndent}) {
+String wrapText(String text, {int? length, int? hangingIndent}) {
if (length == null) return text;
hangingIndent ??= 0;
var splitText = text.split('\n');
@@ -58,12 +58,12 @@
notIndented = wrapTextAsLines(trimmedText,
length: length - leadingWhitespace.length);
}
- String hangingIndentString;
+ String? hangingIndentString;
result.addAll(notIndented.map<String>((String line) {
// Don't return any lines with just whitespace on them.
if (line.isEmpty) return '';
var result = '${hangingIndentString ?? ''}$leadingWhitespace$line';
- hangingIndentString ??= ' ' * hangingIndent;
+ hangingIndentString ??= ' ' * hangingIndent!;
return result;
}));
}
@@ -80,7 +80,7 @@
/// If [length] is not specified, then no wrapping occurs, and the original
/// [text] is returned after splitting it on newlines. Whitespace is not trimmed
/// in this case.
-List<String> wrapTextAsLines(String text, {int start = 0, int length}) {
+List<String> wrapTextAsLines(String text, {int start = 0, int? length}) {
assert(start >= 0);
/// Returns true if the code unit at [index] in [text] is a whitespace
diff --git a/pubspec.yaml b/pubspec.yaml
index 68915fc..48d74f4 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,13 +1,19 @@
name: args
-version: 1.6.1-dev
+version: 2.0.0-nullsafety
homepage: https://github.com/dart-lang/args
description: >-
Library for defining parsers for parsing raw command-line arguments into a set
of options and values using GNU and POSIX style options.
environment:
- sdk: '>=2.3.0 <3.0.0'
+ sdk: '>=2.12.0-0 <3.0.0'
dev_dependencies:
- pedantic: ^1.4.0
- test: '^1.5.1'
+ pedantic: ^1.10.0-nullsafety.3
+ test: ^1.16.0-nullsafety.9
+
+# Required to get a version solve due to cyclic dependency
+dependency_overrides:
+ test_core: ^0.3.12-nullsafety.9
+ coverage: ">=0.13.4 <0.15.0"
+ analyzer: ">=0.39.5 <0.41.0"
diff --git a/test/allow_anything_test.dart b/test/allow_anything_test.dart
index 29038a7..06a4e13 100644
--- a/test/allow_anything_test.dart
+++ b/test/allow_anything_test.dart
@@ -10,7 +10,7 @@
void main() {
group('new ArgParser.allowAnything()', () {
- ArgParser parser;
+ late ArgParser parser;
setUp(() {
parser = ArgParser.allowAnything();
});
@@ -49,10 +49,11 @@
var commandParser = ArgParser()..addCommand('command', parser);
var results =
commandParser.parse(['command', '--foo', '-abc', '--', 'bar']);
- expect(results.command.options, isEmpty);
- expect(results.command.rest, equals(['--foo', '-abc', '--', 'bar']));
- expect(results.command.arguments, equals(['--foo', '-abc', '--', 'bar']));
- expect(results.command.name, equals('command'));
+ expect(results.command!.options, isEmpty);
+ expect(results.command!.rest, equals(['--foo', '-abc', '--', 'bar']));
+ expect(
+ results.command!.arguments, equals(['--foo', '-abc', '--', 'bar']));
+ expect(results.command!.name, equals('command'));
});
test('works as a subcommand in a CommandRunner', () async {
diff --git a/test/args_test.dart b/test/args_test.dart
index b5dd3ee..f101f90 100644
--- a/test/args_test.dart
+++ b/test/args_test.dart
@@ -86,7 +86,7 @@
test('throws ArgumentError if the abbreviation is an invalid value', () {
var parser = ArgParser();
- for (var name in _invalidOptions.where((v) => v != null)) {
+ for (var name in _invalidOptions) {
throwsIllegalArg(() => parser.addOption('flummox', abbr: name));
}
});
@@ -296,25 +296,25 @@
parser.addMultiOption('multi-no');
parser.addMultiOption('multi-def', defaultsTo: ['def']);
- expect(parser.options['flag-no'].getOrDefault(null), equals(null));
- expect(parser.options['flag-no'].getOrDefault(false), equals(false));
- expect(parser.options['flag-def'].getOrDefault(null), equals(true));
- expect(parser.options['flag-def'].getOrDefault(false), equals(false));
- expect(parser.options['single-no'].getOrDefault(null), equals(null));
- expect(parser.options['single-no'].getOrDefault('v'), equals('v'));
- expect(parser.options['single-def'].getOrDefault(null), equals('def'));
- expect(parser.options['single-def'].getOrDefault('v'), equals('v'));
- expect(parser.options['allow-multi-no'].getOrDefault(null), equals([]));
+ expect(parser.options['flag-no']!.getOrDefault(null), equals(null));
+ expect(parser.options['flag-no']!.getOrDefault(false), equals(false));
+ expect(parser.options['flag-def']!.getOrDefault(null), equals(true));
+ expect(parser.options['flag-def']!.getOrDefault(false), equals(false));
+ expect(parser.options['single-no']!.getOrDefault(null), equals(null));
+ expect(parser.options['single-no']!.getOrDefault('v'), equals('v'));
+ expect(parser.options['single-def']!.getOrDefault(null), equals('def'));
+ expect(parser.options['single-def']!.getOrDefault('v'), equals('v'));
+ expect(parser.options['allow-multi-no']!.getOrDefault(null), equals([]));
expect(
- parser.options['allow-multi-no'].getOrDefault(['v']), equals(['v']));
- expect(parser.options['allow-multi-def'].getOrDefault(null),
+ parser.options['allow-multi-no']!.getOrDefault(['v']), equals(['v']));
+ expect(parser.options['allow-multi-def']!.getOrDefault(null),
equals(['def']));
- expect(
- parser.options['allow-multi-def'].getOrDefault(['v']), equals(['v']));
- expect(parser.options['multi-no'].getOrDefault(null), equals([]));
- expect(parser.options['multi-no'].getOrDefault(['v']), equals(['v']));
- expect(parser.options['multi-def'].getOrDefault(null), equals(['def']));
- expect(parser.options['multi-def'].getOrDefault(['v']), equals(['v']));
+ expect(parser.options['allow-multi-def']!.getOrDefault(['v']),
+ equals(['v']));
+ expect(parser.options['multi-no']!.getOrDefault(null), equals([]));
+ expect(parser.options['multi-no']!.getOrDefault(['v']), equals(['v']));
+ expect(parser.options['multi-def']!.getOrDefault(null), equals(['def']));
+ expect(parser.options['multi-def']!.getOrDefault(['v']), equals(['v']));
});
});
}
diff --git a/test/command_parse_test.dart b/test/command_parse_test.dart
index afa8ebe..c0a2cf2 100644
--- a/test/command_parse_test.dart
+++ b/test/command_parse_test.dart
@@ -36,7 +36,7 @@
var args = parser.parse(['install']);
- expect(args.command.name, equals('install'));
+ expect(args.command!.name, equals('install'));
expect(args.rest, isEmpty);
});
@@ -46,7 +46,7 @@
command.addOption('path');
var args = parser.parse(['install', '--path', 'some/path']);
- expect(args.command['path'], equals('some/path'));
+ expect(args.command!['path'], equals('some/path'));
});
test('parses a parent solo option before the command', () {
@@ -56,7 +56,7 @@
var args = parser.parse(['-m', 'debug', 'install']);
expect(args['mode'], equals('debug'));
- expect(args.command.name, equals('install'));
+ expect(args.command!.name, equals('install'));
});
test('parses a parent solo option after the command', () {
@@ -66,7 +66,7 @@
var args = parser.parse(['install', '-m', 'debug']);
expect(args['mode'], equals('debug'));
- expect(args.command.name, equals('install'));
+ expect(args.command!.name, equals('install'));
});
test('parses a parent option before the command', () {
@@ -76,7 +76,7 @@
var args = parser.parse(['--verbose', 'install']);
expect(args['verbose'], isTrue);
- expect(args.command.name, equals('install'));
+ expect(args.command!.name, equals('install'));
});
test('parses a parent option after the command', () {
@@ -86,7 +86,7 @@
var args = parser.parse(['install', '--verbose']);
expect(args['verbose'], isTrue);
- expect(args.command.name, equals('install'));
+ expect(args.command!.name, equals('install'));
});
test('parses a parent negated option before the command', () {
@@ -96,7 +96,7 @@
var args = parser.parse(['--no-verbose', 'install']);
expect(args['verbose'], isFalse);
- expect(args.command.name, equals('install'));
+ expect(args.command!.name, equals('install'));
});
test('parses a parent negated option after the command', () {
@@ -106,7 +106,7 @@
var args = parser.parse(['install', '--no-verbose']);
expect(args['verbose'], isFalse);
- expect(args.command.name, equals('install'));
+ expect(args.command!.name, equals('install'));
});
test('parses a parent abbreviation before the command', () {
@@ -118,7 +118,7 @@
var args = parser.parse(['-dv', 'install']);
expect(args['debug'], isTrue);
expect(args['verbose'], isTrue);
- expect(args.command.name, equals('install'));
+ expect(args.command!.name, equals('install'));
});
test('parses a parent abbreviation after the command', () {
@@ -130,7 +130,7 @@
var args = parser.parse(['install', '-dv']);
expect(args['debug'], isTrue);
expect(args['verbose'], isTrue);
- expect(args.command.name, equals('install'));
+ expect(args.command!.name, equals('install'));
});
test('does not parse a solo command option before the command', () {
@@ -168,10 +168,10 @@
var args = parser.parse(['cmd', 'subcmd', '-abc']);
expect(args['apple'], isTrue);
- expect(args.command.name, equals('cmd'));
- expect(args.command['banana'], isTrue);
- expect(args.command.command.name, equals('subcmd'));
- expect(args.command.command['cherry'], isTrue);
+ expect(args.command!.name, equals('cmd'));
+ expect(args.command!['banana'], isTrue);
+ expect(args.command!.command!.name, equals('subcmd'));
+ expect(args.command!.command!['cherry'], isTrue);
});
test('option is given to innermost command that can take it', () {
@@ -183,9 +183,9 @@
var args = parser.parse(['cmd', 'subcmd', '--verbose']);
expect(args['verbose'], isFalse);
- expect(args.command.name, equals('cmd'));
- expect(args.command['verbose'], isTrue);
- expect(args.command.command.name, equals('subcmd'));
+ expect(args.command!.name, equals('cmd'));
+ expect(args.command!['verbose'], isTrue);
+ expect(args.command!.command!.name, equals('subcmd'));
});
test('remaining arguments are given to the innermost command', () {
@@ -193,11 +193,11 @@
parser.addCommand('cmd')..addCommand('subcmd');
var args = parser.parse(['cmd', 'subcmd', 'other', 'stuff']);
- expect(args.command.name, equals('cmd'));
+ expect(args.command!.name, equals('cmd'));
expect(args.rest, isEmpty);
- expect(args.command.command.name, equals('subcmd'));
- expect(args.command.rest, isEmpty);
- expect(args.command.command.rest, equals(['other', 'stuff']));
+ expect(args.command!.command!.name, equals('subcmd'));
+ expect(args.command!.rest, isEmpty);
+ expect(args.command!.command!.rest, equals(['other', 'stuff']));
});
});
}
diff --git a/test/command_runner_test.dart b/test/command_runner_test.dart
index ce48401..ed29e08 100644
--- a/test/command_runner_test.dart
+++ b/test/command_runner_test.dart
@@ -19,7 +19,7 @@
Run "test help <command>" for more information about a command.''';
void main() {
- var runner;
+ late var runner;
setUp(() {
runner = CommandRunner('test', 'A test command runner.');
});
diff --git a/test/command_test.dart b/test/command_test.dart
index a5c4494..d4be4ae 100644
--- a/test/command_test.dart
+++ b/test/command_test.dart
@@ -7,7 +7,7 @@
import 'test_utils.dart';
void main() {
- var foo;
+ late var foo;
setUp(() {
foo = FooCommand();
diff --git a/test/parse_test.dart b/test/parse_test.dart
index 9639567..1c50c4a 100644
--- a/test/parse_test.dart
+++ b/test/parse_test.dart
@@ -154,12 +154,11 @@
});
test('are invoked even if the option is not present', () {
- var a = 'not called';
var parser = ArgParser();
- parser.addOption('a', callback: (value) => a = value);
+ parser.addOption('a',
+ callback: expectAsync1((value) => expect(value, isNull)));
parser.parse([]);
- expect(a, isNull);
});
group('with allowMultiple', () {
diff --git a/test/test_utils.dart b/test/test_utils.dart
index 68b96d3..5136f48 100644
--- a/test/test_utils.dart
+++ b/test/test_utils.dart
@@ -223,7 +223,7 @@
}
}
-void throwsIllegalArg(function, {String reason}) {
+void throwsIllegalArg(function, {String? reason}) {
expect(function, throwsArgumentError, reason: reason);
}
@@ -231,11 +231,7 @@
expect(() => parser.parse(args), throwsFormatException);
}
-Matcher throwsUsageException(message, usage) {
- return throwsA(predicate((error) {
- expect(error, TypeMatcher<UsageException>());
- expect(error.message, message);
- expect(error.usage, usage);
- return true;
- }));
-}
+Matcher throwsUsageException(String message, String usage) =>
+ throwsA(isA<UsageException>()
+ .having((e) => e.message, 'message', message)
+ .having((e) => e.usage, 'usage', usage));
diff --git a/test/trailing_options_test.dart b/test/trailing_options_test.dart
index 174a1d0..323f582 100644
--- a/test/trailing_options_test.dart
+++ b/test/trailing_options_test.dart
@@ -12,7 +12,7 @@
});
group('when trailing options are allowed', () {
- var parser;
+ late var parser;
setUp(() {
parser = ArgParser(allowTrailingOptions: true);
});
@@ -93,7 +93,7 @@
results = parser.parse(['cmd', '-f', 'a', '-v', '--unknown']);
expect(results['flag'], isTrue); // Not trailing.
- expect(results.command['verbose'], isFalse);
- expect(results.command.rest, equals(['a', '-v', '--unknown']));
+ expect(results.command!['verbose'], isFalse);
+ expect(results.command!.rest, equals(['a', '-v', '--unknown']));
});
}
diff --git a/test/usage_test.dart b/test/usage_test.dart
index 45818b9..3b63d45 100644
--- a/test/usage_test.dart
+++ b/test/usage_test.dart
@@ -502,7 +502,7 @@
// Count the indentation of the last line.
var whitespace = RegExp('^ *');
- var indent = whitespace.firstMatch(lines[lines.length - 1])[0].length;
+ var indent = whitespace.firstMatch(lines[lines.length - 1])![0]!.length;
// Drop the last line. It only exists for specifying indentation.
lines.removeLast();