Require Dart 2.18, update lints, update CI, add dependabot (#222)

diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 0000000..71cdeea
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,10 @@
+# Set update schedule for GitHub Actions
+# See https://docs.github.com/en/github/administering-a-repository/keeping-your-actions-up-to-date-with-dependabot
+
+version: 2
+updates:
+
+- package-ecosystem: "github-actions"
+  directory: "/"
+  schedule:
+    interval: "monthly"
diff --git a/.github/workflows/test-package.yml b/.github/workflows/test-package.yml
index 0ca4263..c75daff 100644
--- a/.github/workflows/test-package.yml
+++ b/.github/workflows/test-package.yml
@@ -21,8 +21,8 @@
       matrix:
         sdk: [dev]
     steps:
-      - uses: actions/checkout@v2
-      - uses: dart-lang/setup-dart@v1.0
+      - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8
+      - uses: dart-lang/setup-dart@6a218f2413a3e78e9087f638a238f6b40893203d
         with:
           sdk: ${{ matrix.sdk }}
       - id: install
@@ -47,10 +47,10 @@
         # Add macos-latest and/or windows-latest if relevant for this package
         os: [ubuntu-latest]
         # Add stable if the package should also be tested on stable
-        sdk: [2.12.0, dev]
+        sdk: [2.18.0, dev]
     steps:
-      - uses: actions/checkout@v2
-      - uses: dart-lang/setup-dart@v1.0
+      - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8
+      - uses: dart-lang/setup-dart@6a218f2413a3e78e9087f638a238f6b40893203d
         with:
           sdk: ${{ matrix.sdk }}
       - id: install
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d3b5eb4..78adc8d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 2.3.2-dev
+
+* Require Dart 2.18
+
 ## 2.3.1
 
 * Switch to using package:lints.
diff --git a/analysis_options.yaml b/analysis_options.yaml
index 106b824..a9dbdb5 100644
--- a/analysis_options.yaml
+++ b/analysis_options.yaml
@@ -1,32 +1,27 @@
+# https://dart.dev/guides/language/analysis-options
 include: package:lints/recommended.yaml
 
 linter:
   rules:
-    - avoid_null_checks_in_equality_operators
+    - always_declare_return_types
+    - avoid_dynamic_calls
     - avoid_unused_constructor_parameters
-    - await_only_futures
-    - camel_case_types
     - cancel_subscriptions
     - comment_references
-    - constant_identifier_names
-    - control_flow_in_finally
     - directives_ordering
-    - empty_statements
-    - hash_and_equals
-    - implementation_imports
-    - iterable_contains_unrelated_type
-    - library_names
-    - library_prefixes
-    - list_remove_unrelated_type
-    - non_constant_identifier_names
-    - overridden_fields
+    - lines_longer_than_80_chars
+    - literal_only_boolean_expressions
+    - missing_whitespace_between_adjacent_strings
+    - no_adjacent_strings_in_list
+    - no_runtimeType_toString
+    - omit_local_variable_types
     - package_api_docs
-    - package_names
-    - package_prefixed_library_names
-    - prefer_final_fields
-    - prefer_generic_function_type_aliases
-    - prefer_typing_uninitialized_variables
+    - prefer_relative_imports
+    - prefer_single_quotes
     - test_types_in_equals
     - throw_in_finally
-    - unnecessary_brace_in_string_interps
-    - unrelated_type_equality_checks
+    - type_annotate_public_apis
+    - unawaited_futures
+    - unnecessary_await_in_return
+    - unnecessary_lambdas
+    - use_super_parameters
diff --git a/example/arg_parser/example.dart b/example/arg_parser/example.dart
index 9a231e8..2123cc4 100644
--- a/example/arg_parser/example.dart
+++ b/example/arg_parser/example.dart
@@ -55,7 +55,6 @@
       allowedHelp: {
         'vm': 'Run Dart code on the standalone dart vm.',
         'd8': 'Run JavaScript from the command line using v8.',
-        // TODO(antonm): rename flag.
         'drt': 'Run Dart or JavaScript in the headless version of Chrome,\n'
             'content shell.',
         'dartium': 'Run Dart or JavaScript in Dartium.',
diff --git a/example/command_runner/draw.dart b/example/command_runner/draw.dart
index d14ed4a..3f0baec 100644
--- a/example/command_runner/draw.dart
+++ b/example/command_runner/draw.dart
@@ -34,7 +34,7 @@
   @override
   FutureOr<String>? run() {
     final size = int.parse(argResults?['size'] ?? '20');
-    final char = globalResults?['char']?[0] ?? '#';
+    final char = (globalResults?['char'] as String?)?[0] ?? '#';
     return draw(size, size, char, (x, y) => true);
   }
 }
@@ -56,7 +56,7 @@
   @override
   FutureOr<String>? run() {
     final size = 2 * int.parse(argResults?['radius'] ?? '10');
-    final char = globalResults?['char']?[0] ?? '#';
+    final char = (globalResults?['char'] as String?)?[0] ?? '#';
     return draw(size, size, char, (x, y) => x * x + y * y < 1);
   }
 }
@@ -94,7 +94,7 @@
   @override
   FutureOr<String>? run() {
     final size = int.parse(argResults?['size'] ?? '20');
-    final char = globalResults?['char']?[0] ?? '#';
+    final char = (globalResults?['char'] as String?)?[0] ?? '#';
     return drawTriangle(size, size * sqrt(3) ~/ 2, char);
   }
 }
@@ -118,7 +118,7 @@
   FutureOr<String>? run() {
     final width = int.parse(argResults?['width'] ?? '50');
     final height = int.parse(argResults?['height'] ?? '10');
-    final char = globalResults?['char']?[0] ?? '#';
+    final char = (globalResults?['char'] as String?)?[0] ?? '#';
     return drawTriangle(width, height, char);
   }
 }
@@ -126,9 +126,9 @@
 String draw(
     int width, int height, String char, bool Function(double, double) pixel) {
   final out = StringBuffer();
-  for (int y = 0; y <= height; ++y) {
+  for (var y = 0; y <= height; ++y) {
     final ty = 2 * y / height - 1;
-    for (int x = 0; x <= width; ++x) {
+    for (var x = 0; x <= width; ++x) {
       final tx = 2 * x / width - 1;
       out.write(pixel(tx, ty) ? char : ' ');
     }
diff --git a/lib/command_runner.dart b/lib/command_runner.dart
index 007c597..e2c9192 100644
--- a/lib/command_runner.dart
+++ b/lib/command_runner.dart
@@ -52,13 +52,16 @@
     var usagePrefix = 'Usage:';
     var buffer = StringBuffer();
     buffer.writeln(
-        '$usagePrefix ${_wrap(invocation, hangingIndent: usagePrefix.length)}\n');
+      '$usagePrefix ${_wrap(invocation, hangingIndent: usagePrefix.length)}\n',
+    );
     buffer.writeln(_wrap('Global options:'));
     buffer.writeln('${argParser.usage}\n');
     buffer.writeln(
-        '${_getCommandUsage(_commands, lineLength: argParser.usageLineLength)}\n');
+      '${_getCommandUsage(_commands, lineLength: argParser.usageLineLength)}\n',
+    );
     buffer.write(_wrap(
-        'Run "$executableName help <command>" for more information about a command.'));
+        'Run "$executableName help <command>" for more information about a '
+        'command.'));
     if (usageFooter != null) {
       buffer.write('\n${_wrap(usageFooter!)}');
     }
diff --git a/lib/src/arg_parser_exception.dart b/lib/src/arg_parser_exception.dart
index 56ac851..d727d70 100644
--- a/lib/src/arg_parser_exception.dart
+++ b/lib/src/arg_parser_exception.dart
@@ -9,7 +9,6 @@
   /// This will be empty if the error was on the root parser.
   final List<String> commands;
 
-  ArgParserException(String message, [Iterable<String>? commands])
-      : commands = commands == null ? const [] : List.unmodifiable(commands),
-        super(message);
+  ArgParserException(super.message, [Iterable<String>? commands])
+      : commands = commands == null ? const [] : List.unmodifiable(commands);
 }
diff --git a/lib/src/help_command.dart b/lib/src/help_command.dart
index 9e2f5a3..f04d014 100644
--- a/lib/src/help_command.dart
+++ b/lib/src/help_command.dart
@@ -22,8 +22,6 @@
   bool get hidden => true;
 
   @override
-  // TODO: Remove when https://github.com/dart-lang/linter/issues/2792 is fixed.
-  // ignore: prefer_void_to_null
   Null run() {
     // Show the default help if no command was specified.
     if (argResults!.rest.isEmpty) {
@@ -34,7 +32,7 @@
     // Walk the command tree to show help for the selected command or
     // subcommand.
     var commands = runner!.commands;
-    Command? command;
+    Command<T>? command;
     var commandString = runner!.executableName;
 
     for (var name in argResults!.rest) {
@@ -53,7 +51,7 @@
       }
 
       command = commands[name];
-      commands = command!.subcommands as Map<String, Command<T>>;
+      commands = command!.subcommands;
       commandString += ' $name';
     }
 
diff --git a/lib/src/option.dart b/lib/src/option.dart
index 09e5d20..50a8628 100644
--- a/lib/src/option.dart
+++ b/lib/src/option.dart
@@ -13,7 +13,7 @@
     String? valueHelp,
     Iterable<String>? allowed,
     Map<String, String>? allowedHelp,
-    defaultsTo,
+    Object? defaultsTo,
     Function? callback,
     OptionType type,
     {bool? negatable,
@@ -146,14 +146,14 @@
   /// For single-valued options, it will be [defaultsTo] if set or `null`
   /// otherwise. For multiple-valued options, it will be an empty list or a
   /// list containing [defaultsTo] if set.
-  dynamic valueOrDefault(value) {
+  dynamic valueOrDefault(Object? value) {
     if (value != null) return value;
     if (isMultiple) return defaultsTo ?? <String>[];
     return defaultsTo;
   }
 
   @Deprecated('Use valueOrDefault instead.')
-  dynamic getOrDefault(value) => valueOrDefault(value);
+  dynamic getOrDefault(Object? value) => valueOrDefault(value);
 
   static final _invalidChars = RegExp(r'''[ \t\r\n"'\\/]''');
 }
diff --git a/lib/src/parser.dart b/lib/src/parser.dart
index ed6e2c7..0862ce9 100644
--- a/lib/src/parser.dart
+++ b/lib/src/parser.dart
@@ -103,6 +103,7 @@
 
       var callback = option.callback;
       if (callback == null) return;
+      // ignore: avoid_dynamic_calls
       callback(option.valueOrDefault(parsedOption));
     });
 
@@ -320,7 +321,7 @@
       return;
     }
 
-    var list = results.putIfAbsent(option.name, () => <String>[]);
+    var list = results.putIfAbsent(option.name, () => <String>[]) as List;
 
     if (option.splitCommas) {
       for (var element in value.split(',')) {
diff --git a/lib/src/usage.dart b/lib/src/usage.dart
index f583ece..d97cc2d 100644
--- a/lib/src/usage.dart
+++ b/lib/src/usage.dart
@@ -105,7 +105,8 @@
         _write(2, '(defaults to on)');
       }
     } else if (option.isMultiple) {
-      if (option.defaultsTo != null && option.defaultsTo.isNotEmpty) {
+      if (option.defaultsTo != null &&
+          (option.defaultsTo as Iterable).isNotEmpty) {
         var defaults =
             (option.defaultsTo as List).map((value) => '"$value"').join(', ');
         _write(2, '(defaults to $defaults)');
@@ -137,9 +138,9 @@
 
   String _allowedTitle(Option option, String allowed) {
     var isDefault = option.defaultsTo is List
-        ? option.defaultsTo.contains(allowed)
+        ? (option.defaultsTo as List).contains(allowed)
         : option.defaultsTo == allowed;
-    return '      [$allowed]' + (isDefault ? ' (default)' : '');
+    return '      [$allowed]${isDefault ? ' (default)' : ''}';
   }
 
   List<int> _calculateColumnWidths() {
@@ -234,7 +235,7 @@
 
   String _buildAllowedList(Option option) {
     var isDefault = option.defaultsTo is List
-        ? option.defaultsTo.contains
+        ? (option.defaultsTo as List).contains
         : (value) => value == option.defaultsTo;
 
     var allowedBuffer = StringBuffer();
diff --git a/lib/src/utils.dart b/lib/src/utils.dart
index 648e554..ae5e093 100644
--- a/lib/src/utils.dart
+++ b/lib/src/utils.dart
@@ -21,7 +21,9 @@
 ///
 /// ```dart
 /// var prefix = "Usage: ";
-/// print(prefix + wrapText(invocation, hangingIndent: prefix.length, length: 40));
+/// print(
+///   prefix + wrapText(invocation, hangingIndent: prefix.length, length: 40),
+/// );
 /// ```
 ///
 /// yields:
diff --git a/pubspec.yaml b/pubspec.yaml
index 4fdf565..e99e29b 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,13 +1,13 @@
 name: args
-version: 2.3.1
+version: 2.3.2-dev
 description: >-
  Library for defining parsers for parsing raw command-line arguments into a set
  of options and values using GNU and POSIX style options.
 repository: https://github.com/dart-lang/args
 
 environment:
-  sdk: '>=2.12.0 <3.0.0'
+  sdk: '>=2.18.0 <3.0.0'
 
 dev_dependencies:
-  lints: ^1.0.0
+  lints: ^2.0.0
   test: ^1.16.0
diff --git a/test/parse_performance_test.dart b/test/parse_performance_test.dart
index 210f853..b099379 100644
--- a/test/parse_performance_test.dart
+++ b/test/parse_performance_test.dart
@@ -56,12 +56,15 @@
   print('Parsed $baseSize elements in ${baseTime}ms, '
       '${baseSize * multiplier} elements in ${largeTime}ms.');
 
-  expect(largeTime, lessThan(baseTime * multiplier * 3),
-      reason:
-          'Comparing large data set time ${largeTime}ms to small data set time '
-          '${baseTime}ms. Data set increased ${multiplier}x, time is allowed to '
-          'increase up to ${multiplier * 3}x, but it increased '
-          '${largeTime ~/ baseTime}x.');
+  expect(
+    largeTime,
+    lessThan(baseTime * multiplier * 3),
+    reason:
+        'Comparing large data set time ${largeTime}ms to small data set time '
+        '${baseTime}ms. Data set increased ${multiplier}x, time is allowed to '
+        'increase up to ${multiplier * 3}x, but it increased '
+        '${largeTime ~/ baseTime}x.',
+  );
 }
 
 int _time(void Function() function) {
diff --git a/test/test_utils.dart b/test/test_utils.dart
index c580d71..ab51146 100644
--- a/test/test_utils.dart
+++ b/test/test_utils.dart
@@ -10,8 +10,7 @@
   @override
   String get usageFooter => 'Also, footer!';
 
-  CommandRunnerWithFooter(String executableName, String description)
-      : super(executableName, description);
+  CommandRunnerWithFooter(super.executableName, super.description);
 }
 
 class CommandRunnerWithFooterAndWrapping extends CommandRunner {
@@ -25,12 +24,11 @@
   ArgParser get argParser => _argParser;
   final _argParser = ArgParser(usageLineLength: 40);
 
-  CommandRunnerWithFooterAndWrapping(String executableName, String description)
-      : super(executableName, description);
+  CommandRunnerWithFooterAndWrapping(super.executableName, super.description);
 }
 
 class FooCommand extends Command {
-  var hasRun = false;
+  bool hasRun = false;
 
   @override
   final name = 'foo';
@@ -76,7 +74,7 @@
 }
 
 class Category1Command extends Command {
-  var hasRun = false;
+  bool hasRun = false;
 
   @override
   final name = 'bar';
@@ -97,7 +95,7 @@
 }
 
 class Category2Command extends Command {
-  var hasRun = false;
+  bool hasRun = false;
 
   @override
   final name = 'baz';
@@ -118,7 +116,7 @@
 }
 
 class Category2Command2 extends Command {
-  var hasRun = false;
+  bool hasRun = false;
 
   @override
   final name = 'baz2';
@@ -139,7 +137,7 @@
 }
 
 class MultilineCommand extends Command {
-  var hasRun = false;
+  bool hasRun = false;
 
   @override
   final name = 'multiline';
@@ -157,7 +155,7 @@
 }
 
 class WrappingCommand extends Command {
-  var hasRun = false;
+  bool hasRun = false;
 
   @override
   ArgParser get argParser => _argParser;
@@ -180,7 +178,7 @@
 }
 
 class LongCommand extends Command {
-  var hasRun = false;
+  bool hasRun = false;
 
   @override
   ArgParser get argParser => _argParser;
@@ -190,11 +188,11 @@
   final name = 'long';
 
   @override
-  final description = 'This command has a long description that needs to be '
-          'wrapped sometimes.\nIt has embedded newlines,\n'
-          '     and indented lines that also need to be wrapped and have their '
-          'indentation preserved.\n' +
-      ('0123456789' * 10);
+  final description =
+      'This command has a long description that needs to be wrapped '
+      'sometimes.\nIt has embedded newlines,\n'
+      '     and indented lines that also need to be wrapped and have their '
+      'indentation preserved.\n${'0123456789' * 10}';
 
   @override
   final takesArguments = false;
@@ -211,7 +209,7 @@
 }
 
 class HiddenCommand extends Command {
-  var hasRun = false;
+  bool hasRun = false;
 
   @override
   final name = 'hidden';
@@ -232,7 +230,7 @@
 }
 
 class HiddenCategorizedCommand extends Command {
-  var hasRun = false;
+  bool hasRun = false;
 
   @override
   final name = 'hiddencategorized';
@@ -256,7 +254,7 @@
 }
 
 class AliasedCommand extends Command {
-  var hasRun = false;
+  bool hasRun = false;
 
   @override
   final name = 'aliased';
@@ -277,7 +275,7 @@
 }
 
 class AsyncCommand extends Command {
-  var hasRun = false;
+  bool hasRun = false;
 
   @override
   final name = 'async';
@@ -293,7 +291,7 @@
 }
 
 class AllowAnythingCommand extends Command {
-  var hasRun = false;
+  bool hasRun = false;
 
   @override
   final name = 'allowAnything';
@@ -320,7 +318,7 @@
   String get description => 'A command with a custom name';
 }
 
-void throwsIllegalArg(function, {String? reason}) {
+void throwsIllegalArg(void Function() function, {String? reason}) {
   expect(function, throwsArgumentError, reason: reason);
 }
 
diff --git a/test/usage_test.dart b/test/usage_test.dart
index 6999724..1167186 100644
--- a/test/usage_test.dart
+++ b/test/usage_test.dart
@@ -322,15 +322,19 @@
       parser.addFlag('longNewline',
           help: 'The flag with a really long help text and newlines\n\nthat '
               'will still be wrapped because it is really long.');
-      parser.addFlag('solid',
-          help:
-              'The-flag-with-no-whitespace-that-will-be-wrapped-by-splitting-a-word.');
+      parser.addFlag(
+        'solid',
+        help: 'The-flag-with-no-whitespace-that-will-be-wrapped-by-splitting-a'
+            '-word.',
+      );
       parser.addFlag('longWhitespace',
           help:
-              '           The flag with a really long help text and whitespace at the start.');
+              '           The flag with a really long help text and whitespace '
+              'at the start.');
       parser.addFlag('longTrailspace',
           help:
-              'The flag with a really long help text and whitespace at the end.             ');
+              'The flag with a really long help text and whitespace at the end.'
+              '             ');
       parser.addFlag('small1', help: ' a ');
       parser.addFlag('small2', help: ' a');
       parser.addFlag('small3', help: 'a ');
@@ -364,9 +368,11 @@
           help:
               'The flag with a really long help text and newlines\n\nthat will '
               'still be wrapped because it is really long.');
-      parser.addFlag('solid',
-          help:
-              'The-flag-with-no-whitespace-that-will-be-wrapped-by-splitting-a-word.');
+      parser.addFlag(
+        'solid',
+        help: 'The-flag-with-no-whitespace-that-will-be-wrapped-by-splitting-a-'
+            'word.',
+      );
       parser.addFlag('small1', help: ' a ');
       parser.addFlag('small2', help: ' a');
       parser.addFlag('small3', help: 'a ');
@@ -494,12 +500,11 @@
 }
 
 void validateUsage(ArgParser parser, String expected) {
-  expected = unindentString(expected);
+  expected = _unindentString(expected);
   expect(parser.usage, equals(expected));
 }
 
-// TODO(rnystrom): Replace one in test_utils.
-String unindentString(String text) {
+String _unindentString(String text) {
   var lines = text.split('\n');
 
   // Count the indentation of the last line.
diff --git a/test/utils_test.dart b/test/utils_test.dart
index 50a4f9f..3cc45b8 100644
--- a/test/utils_test.dart
+++ b/test/utils_test.dart
@@ -7,13 +7,12 @@
 
 const _lineLength = 40;
 const _longLine = 'This is a long line that needs to be wrapped.';
-final _longLineWithNewlines = 'This is a long line with newlines that\n'
-        'needs to be wrapped.\n\n' +
-    '0123456789' * 5;
+final _longLineWithNewlines =
+    'This is a long line with newlines that\nneeds to be wrapped.\n\n'
+    '${'0123456789' * 5}';
 final _indentedLongLineWithNewlines =
-    '    This is an indented long line with newlines that\n'
-            'needs to be wrapped.\n\tAnd preserves tabs.\n      \n  ' +
-        '0123456789' * 5;
+    '    This is an indented long line with newlines that\nneeds to be wrapped.'
+    '\n\tAnd preserves tabs.\n      \n  ${'0123456789' * 5}';
 const _shortLine = 'Short line.';
 const _indentedLongLine = '    This is an indented long line that needs to be '
     'wrapped and indentation preserved.';
@@ -42,7 +41,7 @@
 0123456789'''));
     });
     test('refuses to wrap to a column smaller than 10 characters', () {
-      expect(wrapText('$_longLine ' + '0123456789' * 4, length: 1), equals('''
+      expect(wrapText('$_longLine ${'0123456789' * 4}', length: 1), equals('''
 This is a
 long line
 that needs
@@ -104,13 +103,14 @@
 Short line.'''));
     });
     test(
-        'handles hangingIndent with two unwrapped lines and the second is empty.',
-        () {
-      expect(wrapText('$_shortLine\n', length: _lineLength, hangingIndent: 6),
-          equals('''
+      'handles hangingIndent with two unwrapped lines and the second is empty.',
+      () {
+        expect(wrapText('$_shortLine\n', length: _lineLength, hangingIndent: 6),
+            equals('''
 Short line.
 '''));
-    });
+      },
+    );
     test('honors hangingIndent parameter on already indented line.', () {
       expect(wrapText(_indentedLongLine, length: _lineLength, hangingIndent: 6),
           equals('''
@@ -151,7 +151,7 @@
 
     test('refuses to wrap to a column smaller than 10 characters', () {
       expect(
-          wrapTextAsLines('$_longLine ' + '0123456789' * 4, length: 1),
+          wrapTextAsLines('$_longLine ${'0123456789' * 4}', length: 1),
           equals([
             'This is a',
             'long line',