Add strict-cast analysis option (#3919)

diff --git a/.gitignore b/.gitignore
index 89fbed2..d359298 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,7 @@
 .buildlog
 .DS_Store
 .idea
+.vscode
 .dart_tool/
 .settings/
 /build/
diff --git a/analysis_options.yaml b/analysis_options.yaml
index 85fbba9..4c23fc5 100644
--- a/analysis_options.yaml
+++ b/analysis_options.yaml
@@ -9,6 +9,9 @@
   exclude:
    - lib/src/third_party/**
 
+  language:
+    strict-casts: true
+
 linter:
   rules:
     - avoid_catching_errors
diff --git a/bin/dependency_services.dart b/bin/dependency_services.dart
index e9d76ac..9fd589d 100644
--- a/bin/dependency_services.dart
+++ b/bin/dependency_services.dart
@@ -16,17 +16,18 @@
 import 'package:pub/src/exit_codes.dart' as exit_codes;
 import 'package:pub/src/io.dart';
 import 'package:pub/src/log.dart' as log;
+import 'package:pub/src/utils.dart';
 
 class _DependencyServicesCommandRunner extends CommandRunner<int>
     implements PubTopLevel {
   @override
-  String get directory => argResults['directory'];
+  String get directory => argResults.option('directory');
 
   @override
-  bool get captureStackChains => argResults['verbose'];
+  bool get captureStackChains => argResults.flag('verbose');
 
   @override
-  bool get trace => argResults['verbose'];
+  bool get trace => argResults.flag('verbose');
 
   ArgResults? _argResults;
 
diff --git a/bin/pub.dart b/bin/pub.dart
old mode 100644
new mode 100755
diff --git a/lib/src/command.dart b/lib/src/command.dart
index b7ee191..2994502 100644
--- a/lib/src/command.dart
+++ b/lib/src/command.dart
@@ -68,7 +68,7 @@
 
   String get directory {
     return (argResults.options.contains('directory')
-            ? argResults['directory']
+            ? argResults.optionWithoutDefault('directory')
             : null) ??
         _pubTopLevel.directory;
   }
diff --git a/lib/src/command/add.dart b/lib/src/command/add.dart
index 06a0260..b8a23cd 100644
--- a/lib/src/command/add.dart
+++ b/lib/src/command/add.dart
@@ -766,21 +766,21 @@
 }
 
 extension on ArgResults {
-  bool get isDev => this['dev'];
-  bool get isDryRun => this['dry-run'];
-  String? get gitUrl => this['git-url'];
-  String? get gitPath => this['git-path'];
-  String? get gitRef => this['git-ref'];
-  String? get hostedUrl => this['hosted-url'];
-  String? get path => this['path'];
-  String? get sdk => this['sdk'];
+  bool get isDev => flag('dev');
+  bool get isDryRun => flag('dry-run');
+  String? get gitUrl => this['git-url'] as String?;
+  String? get gitPath => this['git-path'] as String?;
+  String? get gitRef => this['git-ref'] as String?;
+  String? get hostedUrl => this['hosted-url'] as String?;
+  String? get path => this['path'] as String?;
+  String? get sdk => this['sdk'] as String?;
   bool get hasOldStyleOptions =>
       hasGitOptions ||
       path != null ||
       sdk != null ||
       hostedUrl != null ||
       isDev;
-  bool get shouldPrecompile => this['precompile'];
-  bool get example => this['example'];
+  bool get shouldPrecompile => flag('precompile');
+  bool get example => flag('example');
   bool get hasGitOptions => gitUrl != null || gitRef != null || gitPath != null;
 }
diff --git a/lib/src/command/cache_add.dart b/lib/src/command/cache_add.dart
index 85c2c5d..25508ec 100644
--- a/lib/src/command/cache_add.dart
+++ b/lib/src/command/cache_add.dart
@@ -8,6 +8,7 @@
 
 import '../command.dart';
 import '../log.dart' as log;
+import '../package_name.dart';
 import '../utils.dart';
 
 /// Handles the `cache add` pub command.
@@ -52,7 +53,7 @@
     var constraint = VersionConstraint.any;
     if (argResults['version'] != null) {
       try {
-        constraint = VersionConstraint.parse(argResults['version']);
+        constraint = VersionConstraint.parse(argResults['version'] as String);
       } on FormatException catch (error) {
         usageException(error.message);
       }
@@ -71,14 +72,14 @@
       fail('Package $package has no versions that match $constraint.');
     }
 
-    Future<void> downloadVersion(id) async {
+    Future<void> downloadVersion(PackageId id) async {
       final result = await cache.downloadPackage(id);
       if (!result.didUpdate) {
         log.message('Already cached ${id.name} ${id.version}.');
       }
     }
 
-    if (argResults['all']) {
+    if (argResults.flag('all')) {
       // Install them in ascending order.
       ids.sort((id1, id2) => id1.version.compareTo(id2.version));
       await Future.forEach(ids, downloadVersion);
diff --git a/lib/src/command/cache_clean.dart b/lib/src/command/cache_clean.dart
index 786707d..d981b1f 100644
--- a/lib/src/command/cache_clean.dart
+++ b/lib/src/command/cache_clean.dart
@@ -6,6 +6,7 @@
 import '../command_runner.dart';
 import '../io.dart';
 import '../log.dart' as log;
+import '../utils.dart';
 
 class CacheCleanCommand extends PubCommand {
   @override
@@ -27,7 +28,7 @@
   @override
   Future<void> runProtected() async {
     if (dirExists(cache.rootDir)) {
-      if (argResults['force'] || await confirm('''
+      if (argResults.flag('force') || await confirm('''
 This will remove everything inside ${cache.rootDir}.
 You will have to run `$topLevelProgram pub get` again in each project.
 Are you sure?''')) {
diff --git a/lib/src/command/cache_repair.dart b/lib/src/command/cache_repair.dart
index 1c3cd8c..b0e0b04 100644
--- a/lib/src/command/cache_repair.dart
+++ b/lib/src/command/cache_repair.dart
@@ -38,13 +38,15 @@
 
     if (successes.isNotEmpty) {
       var packages = pluralize('package', successes.length);
-      log.message('Reinstalled ${log.green(successes.length)} $packages.');
+      log.message(
+        'Reinstalled ${log.green(successes.length.toString())} $packages.',
+      );
     }
 
     if (failures.isNotEmpty) {
       var packages = pluralize('package', failures.length);
       var buffer = StringBuffer(
-        'Failed to reinstall ${log.red(failures.length)} $packages:\n',
+        'Failed to reinstall ${log.red(failures.length.toString())} $packages:\n',
       );
 
       for (var failure in failures) {
@@ -62,14 +64,14 @@
     if (globalRepairResults.first.isNotEmpty) {
       var packages = pluralize('package', globalRepairResults.first.length);
       log.message(
-        'Reactivated ${log.green(globalRepairResults.first.length)} $packages.',
+        'Reactivated ${log.green(globalRepairResults.first.length.toString())} $packages.',
       );
     }
 
     if (globalRepairResults.last.isNotEmpty) {
       var packages = pluralize('package', globalRepairResults.last.length);
       log.message(
-        'Failed to reactivate ${log.red(globalRepairResults.last.length)} $packages:',
+        'Failed to reactivate ${log.red(globalRepairResults.last.length.toString())} $packages:',
       );
       log.message(
         globalRepairResults.last
diff --git a/lib/src/command/dependency_services.dart b/lib/src/command/dependency_services.dart
index ced8c00..44ae700 100644
--- a/lib/src/command/dependency_services.dart
+++ b/lib/src/command/dependency_services.dart
@@ -69,8 +69,10 @@
     } else {
       final resolution = await _tryResolve(entrypoint.root.pubspec, cache) ??
           (throw DataException('Failed to resolve pubspec'));
-      currentPackages =
-          Map<String, PackageId>.fromIterable(resolution, key: (e) => e.name);
+      currentPackages = Map<String, PackageId>.fromIterable(
+        resolution,
+        key: (e) => (e as PackageId).name,
+      );
     }
     currentPackages.remove(entrypoint.root.name);
 
@@ -361,13 +363,13 @@
     YamlEditor(readTextFile(entrypoint.pubspecPath));
     final toApply = <_PackageVersion>[];
     final input = json.decode(await utf8.decodeStream(stdin));
-    for (final change in input['dependencyChanges']) {
+    for (final change in input['dependencyChanges'] as Iterable) {
       toApply.add(
         _PackageVersion(
-          change['name'],
-          change['version'],
+          change['name'] as String,
+          change['version'] as String?,
           change['constraint'] != null
-              ? VersionConstraint.parse(change['constraint'])
+              ? VersionConstraint.parse(change['constraint'] as String)
               : null,
         ),
       );
@@ -421,23 +423,23 @@
       }
       if (lockFileEditor != null) {
         if (targetVersion != null &&
-            lockFileYaml['packages'].containsKey(targetPackage)) {
+            (lockFileYaml['packages'] as Map).containsKey(targetPackage)) {
           lockFileEditor.update(
             ['packages', targetPackage, 'version'],
             targetVersion.toString(),
           );
           // Remove the now outdated content-hash - it will be restored below
           // after resolution.
-          if (lockFileEditor
-              .parseAt(['packages', targetPackage, 'description'])
-              .value
-              .containsKey('sha256')) {
+          var packageMap = lockFileEditor
+              .parseAt(['packages', targetPackage, 'description']).value as Map;
+          var hasSha = packageMap.containsKey('sha256');
+          if (hasSha) {
             lockFileEditor.remove(
               ['packages', targetPackage, 'description', 'sha256'],
             );
           }
         } else if (targetRevision != null &&
-            lockFileYaml['packages'].containsKey(targetPackage)) {
+            (lockFileYaml['packages'] as Map).containsKey(targetPackage)) {
           final ref = entrypoint.lockFile.packages[targetPackage]!.toRef();
           final currentDescription = ref.description as GitDescription;
           final updatedRef = PackageRef(
@@ -468,7 +470,7 @@
           );
         } else if (targetVersion == null &&
             targetRevision == null &&
-            !lockFileYaml['packages'].containsKey(targetPackage)) {
+            !(lockFileYaml['packages'] as Map).containsKey(targetPackage)) {
           dataError(
             'Trying to remove non-existing transitive dependency $targetPackage.',
           );
diff --git a/lib/src/command/deps.dart b/lib/src/command/deps.dart
index 0fe86c2..a3e2be8 100644
--- a/lib/src/command/deps.dart
+++ b/lib/src/command/deps.dart
@@ -32,7 +32,7 @@
   bool get takesArguments => false;
 
   /// Whether to include dev dependencies.
-  bool get _includeDev => argResults['dev'];
+  bool get _includeDev => argResults.flag('dev');
 
   DepsCommand() {
     argParser.addOption(
@@ -75,7 +75,7 @@
     await entrypoint.ensureUpToDate();
     final buffer = StringBuffer();
 
-    if (argResults['json']) {
+    if (argResults.flag('json')) {
       if (argResults.wasParsed('dev')) {
         usageException(
           'Cannot combine --json and --dev.\nThe json output contains the dependency type in the output.',
@@ -150,7 +150,7 @@
         ),
       );
     } else {
-      if (argResults['executables']) {
+      if (argResults.flag('executables')) {
         _outputExecutables(buffer);
       } else {
         for (var sdk in sdks.values) {
@@ -174,7 +174,7 @@
       }
     }
 
-    log.message(buffer);
+    log.message(buffer.toString());
   }
 
   /// Outputs a list of all of the package's immediate, dev, override, and
diff --git a/lib/src/command/downgrade.dart b/lib/src/command/downgrade.dart
index e92b894..8c7a17d 100644
--- a/lib/src/command/downgrade.dart
+++ b/lib/src/command/downgrade.dart
@@ -7,6 +7,7 @@
 import '../command.dart';
 import '../log.dart' as log;
 import '../solver.dart';
+import '../utils.dart';
 
 /// Handles the `downgrade` pub command.
 class DowngradeCommand extends PubCommand {
@@ -21,7 +22,7 @@
   String get docUrl => 'https://dart.dev/tools/pub/cmd/pub-downgrade';
 
   @override
-  bool get isOffline => argResults['offline'];
+  bool get isOffline => argResults.flag('offline');
 
   DowngradeCommand() {
     argParser.addFlag(
@@ -62,7 +63,7 @@
         ),
       );
     }
-    var dryRun = argResults['dry-run'];
+    var dryRun = argResults.flag('dry-run');
 
     await entrypoint.acquireDependencies(
       SolveType.downgrade,
@@ -71,7 +72,7 @@
       analytics: analytics,
     );
     var example = entrypoint.example;
-    if (argResults['example'] && example != null) {
+    if (argResults.flag('example') && example != null) {
       await example.acquireDependencies(
         SolveType.get,
         unlock: argResults.rest,
diff --git a/lib/src/command/get.dart b/lib/src/command/get.dart
index 51419c1..2544282 100644
--- a/lib/src/command/get.dart
+++ b/lib/src/command/get.dart
@@ -7,6 +7,7 @@
 import '../command.dart';
 import '../log.dart' as log;
 import '../solver.dart';
+import '../utils.dart';
 
 /// Handles the `get` pub command.
 class GetCommand extends PubCommand {
@@ -17,7 +18,7 @@
   @override
   String get docUrl => 'https://dart.dev/tools/pub/cmd/pub-get';
   @override
-  bool get isOffline => argResults['offline'];
+  bool get isOffline => argResults.flag('offline');
   @override
   String get argumentsDescription => '';
 
@@ -75,21 +76,21 @@
 
     await entrypoint.acquireDependencies(
       SolveType.get,
-      dryRun: argResults['dry-run'],
-      precompile: argResults['precompile'],
+      dryRun: argResults.flag('dry-run'),
+      precompile: argResults.flag('precompile'),
       analytics: analytics,
-      enforceLockfile: argResults['enforce-lockfile'],
+      enforceLockfile: argResults.flag('enforce-lockfile'),
     );
 
     var example = entrypoint.example;
-    if (argResults['example'] && example != null) {
+    if ((argResults['example'] as bool? ?? false) && example != null) {
       await example.acquireDependencies(
         SolveType.get,
-        dryRun: argResults['dry-run'],
-        precompile: argResults['precompile'],
+        dryRun: argResults.flag('dry-run'),
+        precompile: argResults.flag('precompile'),
         analytics: analytics,
         summaryOnly: true,
-        enforceLockfile: argResults['enforce-lockfile'],
+        enforceLockfile: argResults.flag('enforce-lockfile'),
       );
     }
   }
diff --git a/lib/src/command/global_activate.dart b/lib/src/command/global_activate.dart
index 8fdbc46..2080894 100644
--- a/lib/src/command/global_activate.dart
+++ b/lib/src/command/global_activate.dart
@@ -87,8 +87,8 @@
         usageException('Cannot pass both --no-executables and --executable.');
       }
 
-      executables = argResults['executable'];
-    } else if (argResults['no-executables']) {
+      executables = argResults['executable'] as List<String>?;
+    } else if (argResults.flag('no-executables')) {
       // An empty list means no executables.
       executables = [];
     }
@@ -126,8 +126,8 @@
           repo,
           executables,
           overwriteBinStubs: overwrite,
-          path: argResults['git-path'],
-          ref: argResults['git-ref'],
+          path: argResults['git-path'] as String?,
+          ref: argResults['git-ref'] as String?,
         );
 
       case 'hosted':
@@ -135,7 +135,8 @@
 
         PackageRef ref;
         try {
-          ref = cache.hosted.refFor(package, url: argResults['hosted-url']);
+          ref = cache.hosted
+              .refFor(package, url: argResults['hosted-url'] as String?);
         } on FormatException catch (e) {
           usageException('Invalid hosted-url: $e');
         }
diff --git a/lib/src/command/global_run.dart b/lib/src/command/global_run.dart
index c7e615b..c6e996f 100644
--- a/lib/src/command/global_run.dart
+++ b/lib/src/command/global_run.dart
@@ -79,7 +79,8 @@
       Executable.adaptProgramName(package, executable),
       args,
       vmArgs: vmArgs,
-      enableAsserts: argResults['enable-asserts'] || argResults['checked'],
+      enableAsserts:
+          argResults.flag('enable-asserts') || argResults.flag('checked'),
       recompile: (executable) => log.errorsOnlyUnlessTerminal(
         () => globalEntrypoint.precompileExecutable(executable),
       ),
diff --git a/lib/src/command/lish.dart b/lib/src/command/lish.dart
index 294df13..9c4372c 100644
--- a/lib/src/command/lish.dart
+++ b/lib/src/command/lish.dart
@@ -40,7 +40,7 @@
     // An explicit argument takes precedence.
     if (argResults.wasParsed('server')) {
       try {
-        return validateAndNormalizeHostedUrl(argResults['server']);
+        return validateAndNormalizeHostedUrl(argResults.option('server'));
       } on FormatException catch (e) {
         usageException('Invalid server: $e');
       }
@@ -61,12 +61,12 @@
   }();
 
   /// Whether the publish is just a preview.
-  bool get dryRun => argResults['dry-run'];
+  bool get dryRun => argResults.flag('dry-run');
 
   /// Whether the publish requires confirmation.
-  bool get force => argResults['force'];
+  bool get force => argResults.flag('force');
 
-  bool get skipValidation => argResults['skip-validation'];
+  bool get skipValidation => argResults.flag('skip-validation');
 
   LishCommand() {
     argParser.addFlag(
@@ -134,7 +134,7 @@
           if (fields is! Map) invalidServerResponse(parametersResponse);
           fields.forEach((key, value) {
             if (value is! String) invalidServerResponse(parametersResponse);
-            request.fields[key] = value;
+            request.fields[key as String] = value;
           });
 
           request.followRedirects = false;
diff --git a/lib/src/command/login.dart b/lib/src/command/login.dart
index 670d1a8..68ec850 100644
--- a/lib/src/command/login.dart
+++ b/lib/src/command/login.dart
@@ -47,11 +47,17 @@
     return await oauth2.withClient((client) async {
       final discovery = await oauth2.fetchOidcDiscoveryDocument();
       final userInfoEndpoint = discovery['userinfo_endpoint'];
+      if (userInfoEndpoint is! String) {
+        log.fine(
+          'Bad discovery document. userinfo_endpoint not a String',
+        );
+        return null;
+      }
       final userInfoRequest = await client.get(Uri.parse(userInfoEndpoint));
       if (userInfoRequest.statusCode != 200) return null;
       try {
         final userInfo = json.decode(userInfoRequest.body);
-        final name = userInfo['name'];
+        final name = userInfo['name'] as String?;
         final email = userInfo['email'];
         if (email is String) {
           return _UserInfo(name, email);
diff --git a/lib/src/command/outdated.dart b/lib/src/command/outdated.dart
index dee80a1..050cedc 100644
--- a/lib/src/command/outdated.dart
+++ b/lib/src/command/outdated.dart
@@ -39,7 +39,8 @@
 
   /// Avoid showing spinning progress messages when not in a terminal, and
   /// when we are outputting machine-readable json.
-  bool get _shouldShowSpinner => terminalOutputForStdout && !argResults['json'];
+  bool get _shouldShowSpinner =>
+      terminalOutputForStdout && !argResults.flag('json');
 
   @override
   bool get takesArguments => false;
@@ -118,9 +119,9 @@
     }
     final mode = _OutdatedMode();
 
-    final includeDevDependencies = argResults['dev-dependencies'];
-    final includeDependencyOverrides = argResults['dependency-overrides'];
-    if (argResults['json'] && argResults.wasParsed('transitive')) {
+    final includeDevDependencies = argResults.flag('dev-dependencies');
+    final includeDependencyOverrides = argResults.flag('dependency-overrides');
+    if (argResults.flag('json') && argResults.wasParsed('transitive')) {
       usageException('Cannot specify both `--json` and `--transitive`\n'
           'The json report always includes transitive dependencies.');
     }
@@ -288,8 +289,9 @@
 
     rows.sort();
 
-    final showAll = argResults['show-all'] || argResults['up-to-date'];
-    if (argResults['json']) {
+    final showAll =
+        argResults.flag('show-all') || argResults.flag('up-to-date');
+    if (argResults.flag('json')) {
       await _outputJson(
         rows,
         mode,
@@ -321,7 +323,7 @@
   }
 
   bool get showTransitiveDependencies {
-    return argResults['transitive'];
+    return argResults.flag('transitive');
   }
 
   late final bool prereleases = () {
@@ -330,10 +332,10 @@
     // 'pre-releases'.
     // Otherwise fall back to the default implied by the mode.
     if (argResults.wasParsed('prereleases')) {
-      return argResults['prereleases'];
+      return argResults.flag('prereleases');
     }
     if (argResults.wasParsed('pre-releases')) {
-      return argResults['pre-releases'];
+      return argResults.flag('pre-releases');
     }
     return false;
   }();
@@ -365,10 +367,7 @@
     Package root,
     Iterable<PackageId> resolution,
   ) async {
-    final nameToId = Map<String, PackageId>.fromIterable(
-      resolution,
-      key: (id) => id.name,
-    );
+    final nameToId = {for (final id in resolution) id.name: id};
 
     final nonDevDependencies = <String>{root.name};
     final queue = [...root.dependencies.keys];
@@ -865,7 +864,7 @@
 _FormattedString _format(
   String value,
   String Function(String) format, {
-  prefix = '',
+  String? prefix = '',
 }) {
   return _FormattedString(value, format: format, prefix: prefix);
 }
@@ -887,10 +886,10 @@
   _MarkedVersionDetails(
     this._versionDetails, {
     required this.asDesired,
-    format,
-    prefix = '',
-    suffix = '',
-    jsonExplanation,
+    String Function(String)? format,
+    String? prefix = '',
+    String? suffix = '',
+    MapEntry<String, Object>? jsonExplanation,
   })  : _format = format,
         _prefix = prefix,
         _suffix = suffix,
@@ -927,8 +926,8 @@
   _FormattedString(
     this.value, {
     String Function(String)? format,
-    prefix,
-    suffix,
+    String? prefix,
+    String? suffix,
   })  : _format = format ?? _noFormat,
         _prefix = prefix ?? '',
         _suffix = suffix ?? '';
diff --git a/lib/src/command/remove.dart b/lib/src/command/remove.dart
index a367588..7063c52 100644
--- a/lib/src/command/remove.dart
+++ b/lib/src/command/remove.dart
@@ -10,6 +10,7 @@
 import '../log.dart' as log;
 import '../pubspec.dart';
 import '../solver.dart';
+import '../utils.dart';
 
 /// Handles the `remove` pub command. Removes dependencies from `pubspec.yaml`,
 /// and performs an operation similar to `pub get`. Unlike `pub add`, this
@@ -33,9 +34,9 @@
   @override
   String get docUrl => 'https://dart.dev/tools/pub/cmd/pub-remove';
   @override
-  bool get isOffline => argResults['offline'];
+  bool get isOffline => argResults.flag('offline');
 
-  bool get isDryRun => argResults['dry-run'];
+  bool get isDryRun => argResults.flag('dry-run');
 
   RemoveCommand() {
     argParser.addFlag(
@@ -92,16 +93,16 @@
 
     await entrypoint.withPubspec(newPubspec).acquireDependencies(
           SolveType.get,
-          precompile: !isDryRun && argResults['precompile'],
+          precompile: !isDryRun && argResults.flag('precompile'),
           dryRun: isDryRun,
           analytics: isDryRun ? null : analytics,
         );
 
     var example = entrypoint.example;
-    if (!isDryRun && argResults['example'] && example != null) {
+    if (!isDryRun && argResults.flag('example') && example != null) {
       await example.acquireDependencies(
         SolveType.get,
-        precompile: argResults['precompile'],
+        precompile: argResults.flag('precompile'),
         summaryOnly: true,
         analytics: analytics,
       );
diff --git a/lib/src/command/run.dart b/lib/src/command/run.dart
index 67920e1..9fe1a03 100644
--- a/lib/src/command/run.dart
+++ b/lib/src/command/run.dart
@@ -101,7 +101,8 @@
       entrypoint,
       Executable.adaptProgramName(package, executable),
       args,
-      enableAsserts: argResults['enable-asserts'] || argResults['checked'],
+      enableAsserts:
+          argResults.flag('enable-asserts') || argResults.flag('checked'),
       recompile: (executable) => log.errorsOnlyUnlessTerminal(
         () => entrypoint.precompileExecutable(executable),
       ),
diff --git a/lib/src/command/token_add.dart b/lib/src/command/token_add.dart
index 5ade7ba..c15a51e 100644
--- a/lib/src/command/token_add.dart
+++ b/lib/src/command/token_add.dart
@@ -37,7 +37,7 @@
   @override
   String get docUrl => 'https://dart.dev/tools/pub/cmd/pub-token';
 
-  String? get envVar => argResults['env-var'];
+  String? get envVar => argResults['env-var'] as String?;
 
   TokenAddCommand() {
     argParser.addOption(
diff --git a/lib/src/command/token_list.dart b/lib/src/command/token_list.dart
index 178d0a4..61b797f 100644
--- a/lib/src/command/token_list.dart
+++ b/lib/src/command/token_list.dart
@@ -22,7 +22,7 @@
         'repositories:',
       );
       for (final token in cache.tokenStore.credentials) {
-        log.message(token.url);
+        log.message(token.url.toString());
       }
     } else {
       log.message(
diff --git a/lib/src/command/token_remove.dart b/lib/src/command/token_remove.dart
index b4a9a77..f3f20c0 100644
--- a/lib/src/command/token_remove.dart
+++ b/lib/src/command/token_remove.dart
@@ -6,6 +6,7 @@
 import '../exceptions.dart';
 import '../log.dart' as log;
 import '../source/hosted.dart';
+import '../utils.dart';
 
 /// Handles the `token remove` pub command.
 class TokenRemoveCommand extends PubCommand {
@@ -19,7 +20,7 @@
   @override
   String get docUrl => 'https://dart.dev/tools/pub/cmd/pub-token';
 
-  bool get isAll => argResults['all'];
+  bool get isAll => argResults.flag('all');
 
   TokenRemoveCommand() {
     argParser.addFlag(
diff --git a/lib/src/command/upgrade.dart b/lib/src/command/upgrade.dart
index 6c1dcda..bbe28ef 100644
--- a/lib/src/command/upgrade.dart
+++ b/lib/src/command/upgrade.dart
@@ -35,7 +35,7 @@
   String get docUrl => 'https://dart.dev/tools/pub/cmd/pub-upgrade';
 
   @override
-  bool get isOffline => argResults['offline'];
+  bool get isOffline => argResults.flag('offline');
 
   UpgradeCommand() {
     argParser.addFlag(
@@ -90,14 +90,14 @@
   /// Avoid showing spinning progress messages when not in a terminal.
   bool get _shouldShowSpinner => terminalOutputForStdout;
 
-  bool get _dryRun => argResults['dry-run'];
+  bool get _dryRun => argResults.flag('dry-run');
 
-  bool get _precompile => argResults['precompile'];
+  bool get _precompile => argResults.flag('precompile');
 
   bool get _upgradeNullSafety =>
-      argResults['nullsafety'] || argResults['null-safety'];
+      argResults.flag('nullsafety') || argResults.flag('null-safety');
 
-  bool get _upgradeMajorVersions => argResults['major-versions'];
+  bool get _upgradeMajorVersions => argResults.flag('major-versions');
 
   @override
   Future<void> runProtected() async {
@@ -114,7 +114,7 @@
     }
 
     if (_upgradeMajorVersions) {
-      if (argResults['example'] && entrypoint.example != null) {
+      if (argResults.flag('example') && entrypoint.example != null) {
         log.warning(
           'Running `upgrade --major-versions` only in `${entrypoint.rootDir}`. Run `$topLevelProgram pub upgrade --major-versions --directory example/` separately.',
         );
@@ -123,7 +123,7 @@
     } else {
       await _runUpgrade(entrypoint);
     }
-    if (argResults['example'] && entrypoint.example != null) {
+    if (argResults.flag('example') && entrypoint.example != null) {
       // Reload the entrypoint to ensure we pick up potential changes that has
       // been made.
       final exampleEntrypoint = Entrypoint(directory, cache).example!;
diff --git a/lib/src/command/uploader.dart b/lib/src/command/uploader.dart
index 28ff85d..e661d72 100644
--- a/lib/src/command/uploader.dart
+++ b/lib/src/command/uploader.dart
@@ -23,7 +23,7 @@
   bool get hidden => true;
 
   /// The URL of the package hosting server.
-  Uri get server => Uri.parse(argResults['server']);
+  Uri get server => Uri.parse(argResults.option('server'));
 
   UploaderCommand() {
     argParser.addOption(
diff --git a/lib/src/command_runner.dart b/lib/src/command_runner.dart
index 5fa9344..f9687fe 100644
--- a/lib/src/command_runner.dart
+++ b/lib/src/command_runner.dart
@@ -32,6 +32,7 @@
 import 'log.dart' as log;
 import 'log.dart';
 import 'sdk.dart';
+import 'utils.dart';
 
 /// The name of the program that is invoking pub
 /// 'flutter' if we are running inside `flutter pub` 'dart' otherwise.
@@ -42,13 +43,13 @@
 
 class PubCommandRunner extends CommandRunner<int> implements PubTopLevel {
   @override
-  String get directory => argResults['directory'];
+  String get directory => argResults.option('directory');
 
   @override
   bool get captureStackChains {
-    return argResults['trace'] ||
-        argResults['verbose'] ||
-        argResults['verbosity'] == 'all';
+    return argResults.flag('trace') ||
+        argResults.flag('verbose') ||
+        argResults.optionWithoutDefault('verbosity') == 'all';
   }
 
   @override
@@ -68,14 +69,14 @@
         return log.Verbosity.all;
       default:
         // No specific verbosity given, so check for the shortcut.
-        if (argResults['verbose']) return log.Verbosity.all;
+        if (argResults.flag('verbose')) return log.Verbosity.all;
         if (runningFromTest) return log.Verbosity.testing;
         return log.Verbosity.normal;
     }
   }
 
   @override
-  bool get trace => argResults['trace'];
+  bool get trace => argResults.flag('trace');
 
   ArgResults? _argResults;
 
@@ -170,7 +171,7 @@
   Future<int?> runCommand(ArgResults topLevelResults) async {
     _checkDepsSynced();
 
-    if (topLevelResults['version']) {
+    if (topLevelResults.flag('version')) {
       log.message('Pub ${sdk.version}');
       return 0;
     }
diff --git a/lib/src/entrypoint.dart b/lib/src/entrypoint.dart
index ab8fed6..cb1ecee 100644
--- a/lib/src/entrypoint.dart
+++ b/lib/src/entrypoint.dart
@@ -148,7 +148,7 @@
     }
     late PackageConfig result;
     try {
-      result = PackageConfig.fromJson(json.decode(packageConfigRaw));
+      result = PackageConfig.fromJson(json.decode(packageConfigRaw) as Object?);
     } on FormatException {
       badPackageConfig();
     }
diff --git a/lib/src/error_group.dart b/lib/src/error_group.dart
index 560d806..8c09552 100644
--- a/lib/src/error_group.dart
+++ b/lib/src/error_group.dart
@@ -106,7 +106,7 @@
   ///
   /// If all members of [this] have already completed successfully or with an
   /// error, it's a [StateError] to try to signal an error.
-  void signalError(var error, [StackTrace? stackTrace]) {
+  void signalError(Object error, [StackTrace? stackTrace]) {
     if (_isDone) {
       throw StateError("Can't signal errors on a complete ErrorGroup.");
     }
@@ -118,7 +118,7 @@
   ///
   /// This is just like [signalError], but instead of throwing an error if
   /// [this] is complete, it just does nothing.
-  void _signalError(var error, [StackTrace? stackTrace]) {
+  void _signalError(Object error, [StackTrace? stackTrace]) {
     if (_isDone) return;
 
     var caught = false;
@@ -136,6 +136,7 @@
     _done._signalError(error, stackTrace);
     if (!caught && !_done._hasListeners) {
       scheduleMicrotask(() {
+        // ignore: only_throw_errors
         throw error;
       });
     }
@@ -234,7 +235,7 @@
 
   /// Signal that an error from [_group] should be propagated through [this],
   /// unless it's already complete.
-  void _signalError(var error, [StackTrace? stackTrace]) {
+  void _signalError(Object error, [StackTrace? stackTrace]) {
     if (!_isDone) _completer.completeError(error, stackTrace);
     _isDone = true;
   }
@@ -308,7 +309,7 @@
 
   /// Signal that an error from [_group] should be propagated through [this],
   /// unless it's already complete.
-  void _signalError(var e, [StackTrace? stackTrace]) {
+  void _signalError(Object e, [StackTrace? stackTrace]) {
     if (_isDone) return;
     _subscription.cancel();
     // Call these asynchronously to work around issue 7913.
diff --git a/lib/src/global_packages.dart b/lib/src/global_packages.dart
index 5ed3438..b9b25b6 100644
--- a/lib/src/global_packages.dart
+++ b/lib/src/global_packages.dart
@@ -615,7 +615,7 @@
         message.writeln('  From ${log.bold(package)}: '
             '${toSentence(executableNames)}');
       });
-      log.error(message);
+      log.error(message.toString());
     }
 
     return Pair(successes, failures);
diff --git a/lib/src/http.dart b/lib/src/http.dart
index ab302fd..732381d 100644
--- a/lib/src/http.dart
+++ b/lib/src/http.dart
@@ -178,13 +178,14 @@
 /// "some message"}}`. If the format is correct, the message will be printed;
 /// otherwise an error will be raised.
 void handleJsonSuccess(http.Response response) {
-  var parsed = parseJsonResponse(response);
-  if (parsed['success'] is! Map ||
-      !parsed['success'].containsKey('message') ||
+  final parsed = parseJsonResponse(response);
+  final success = parsed['success'];
+  if (success is! Map ||
+      !(parsed['success'] as Map).containsKey('message') ||
       parsed['success']['message'] is! String) {
     invalidServerResponse(response);
   }
-  log.message(log.green(parsed['success']['message']));
+  log.message(log.green(parsed['success']['message'] as String));
 }
 
 /// Handles an unsuccessful JSON-formatted response from pub.dev.
@@ -199,12 +200,13 @@
     fail(log.red('Invalid server response'));
   }
   var errorMap = parseJsonResponse(response);
-  if (errorMap['error'] is! Map ||
-      !errorMap['error'].containsKey('message') ||
-      errorMap['error']['message'] is! String) {
+  final error = errorMap['error'];
+  if (error is! Map ||
+      !error.containsKey('message') ||
+      error['message'] is! String) {
     invalidServerResponse(response);
   }
-  fail(log.red(errorMap['error']['message']));
+  fail(log.red(error['message'] as String));
 }
 
 /// Parses a response body, assuming it's JSON-formatted.
@@ -212,9 +214,9 @@
 /// Throws a user-friendly error if the response body is invalid JSON, or if
 /// it's not a map.
 Map parseJsonResponse(http.Response response) {
-  Object value;
+  Object? value;
   try {
-    value = jsonDecode(response.body);
+    value = jsonDecode(response.body) as Object?;
   } on FormatException {
     invalidServerResponse(response);
   }
diff --git a/lib/src/io.dart b/lib/src/io.dart
index 086f93f..386d176 100644
--- a/lib/src/io.dart
+++ b/lib/src/io.dart
@@ -742,7 +742,7 @@
 Future<PubProcessResult> runProcess(
   String executable,
   List<String> args, {
-  workingDir,
+  String? workingDir,
   Map<String, String>? environment,
   bool runInShell = false,
 }) {
@@ -765,8 +765,11 @@
       );
     }
 
-    var pubResult =
-        PubProcessResult(result.stdout, result.stderr, result.exitCode);
+    var pubResult = PubProcessResult(
+      result.stdout as String,
+      result.stderr as String,
+      result.exitCode,
+    );
     log.processResult(executable, pubResult);
     return pubResult;
   });
@@ -833,8 +836,11 @@
   } on IOException catch (e) {
     throw RunProcessException('Pub failed to run subprocess `$executable`: $e');
   }
-  var pubResult =
-      PubProcessResult(result.stdout, result.stderr, result.exitCode);
+  var pubResult = PubProcessResult(
+    result.stdout as String,
+    result.stderr as String,
+    result.exitCode,
+  );
   log.processResult(executable, pubResult);
   return pubResult;
 }
diff --git a/lib/src/lock_file.dart b/lib/src/lock_file.dart
index 48de2fd..b7fe219 100644
--- a/lib/src/lock_file.dart
+++ b/lib/src/lock_file.dart
@@ -55,10 +55,10 @@
     Set<String>? devDependencies,
     Set<String>? overriddenDependencies,
   }) : this._(
-          Map.fromIterable(
-            ids.where((id) => !id.isRoot),
-            key: (id) => id.name,
-          ),
+          {
+            for (final id in ids)
+              if (!id.isRoot) id.name: id
+          },
           sdkConstraints ?? {'dart': SdkConstraint(VersionConstraint.any)},
           mainDependencies ?? const UnmodifiableSetView.empty(),
           devDependencies ?? const UnmodifiableSetView.empty(),
@@ -289,7 +289,7 @@
         return _wrapFormatException(
           'Expected a $typeDescription',
           node.span,
-          () => parse(node.value),
+          () => parse(value),
         );
       } else if (value is T) {
         return value;
@@ -307,7 +307,7 @@
   ) {
     map.nodes.forEach((key, value) {
       f(
-        _parseNode(key, keyTypeDescription),
+        _parseNode(key as YamlNode, keyTypeDescription),
         _parseNode(value, valueTypeDescription),
       );
     });
diff --git a/lib/src/log.dart b/lib/src/log.dart
index 5a1299d..9bc467f 100644
--- a/lib/src/log.dart
+++ b/lib/src/log.dart
@@ -204,33 +204,32 @@
 ///
 /// If [error] is passed, it's appended to [message]. If [trace] is passed, it's
 /// printed at log level fine.
-void error(message, [error, StackTrace? trace]) {
-  message ??= '';
+void error(String message, [error, StackTrace? trace]) {
   if (error != null) {
     message = message.isEmpty ? '$error' : '$message: $error';
     if (error is Error && trace == null) trace = error.stackTrace;
   }
   write(Level.error, message);
-  if (trace != null) write(Level.fine, Chain.forTrace(trace));
+  if (trace != null) write(Level.fine, Chain.forTrace(trace).toString());
 }
 
 /// Logs [message] at [Level.warning].
-void warning(message) => write(Level.warning, message);
+void warning(String message) => write(Level.warning, message);
 
 /// Logs [message] at [Level.message].
-void message(message) => write(Level.message, message);
+void message(String message) => write(Level.message, message);
 
 /// Logs [message] at [Level.io].
-void io(message) => write(Level.io, message);
+void io(String message) => write(Level.io, message);
 
 /// Logs [message] at [Level.solver].
-void solver(message) => write(Level.solver, message);
+void solver(String message) => write(Level.solver, message);
 
 /// Logs [message] at [Level.fine].
-void fine(message) => write(Level.fine, message);
+void fine(String message) => write(Level.fine, message);
 
 /// Logs [message] at [level].
-void write(Level level, message) {
+void write(Level level, String message) {
   message = message.toString();
   var lines = splitLines(message);
 
@@ -290,7 +289,7 @@
 }
 
 /// Logs an exception.
-void exception(exception, [StackTrace? trace]) {
+void exception(Object exception, [StackTrace? trace]) {
   if (exception is SilentException) return;
 
   var chain = trace == null ? Chain.current() : Chain.forTrace(trace);
@@ -314,9 +313,9 @@
   }
 
   if (!isUserFacingException(exception)) {
-    error(chain.terse);
+    error(chain.terse.toString());
   } else {
-    fine(chain.terse);
+    fine(chain.terse.toString());
   }
 
   if (exception is WrappedException && exception.innerError != null) {
@@ -505,38 +504,38 @@
 /// that supports that.
 ///
 /// Use this to highlight something interesting but neither good nor bad.
-String cyan(text) => _addColor(text, _cyan);
+String cyan(String text) => _addColor(text, _cyan);
 
 /// Wraps [text] in the ANSI escape codes to color it green when on a platform
 /// that supports that.
 ///
 /// Use this to highlight something successful or otherwise positive.
-String green(text) => _addColor(text, _green);
+String green(String text) => _addColor(text, _green);
 
 /// Wraps [text] in the ANSI escape codes to color it magenta when on a
 /// platform that supports that.
 ///
 /// Use this to highlight something risky that the user should be aware of but
 /// may intend to do.
-String magenta(text) => _addColor(text, _magenta);
+String magenta(String text) => _addColor(text, _magenta);
 
 /// Wraps [text] in the ANSI escape codes to color it red when on a platform
 /// that supports that.
 ///
 /// Use this to highlight unequivocal errors, problems, or failures.
-String red(text) => _addColor(text, _red);
+String red(String text) => _addColor(text, _red);
 
 /// Wraps [text] in the ANSI escape codes to color it yellow when on a platform
 /// that supports that.
 ///
 /// Use this to highlight warnings, cautions or other things that are bad but
 /// do not prevent the user's goal from being reached.
-String yellow(text) => _addColor(text, _yellow);
+String yellow(String text) => _addColor(text, _yellow);
 
 /// Returns [text] colored using the given [colorCode].
 ///
 /// This is resilient to the text containing other colors or bold text.
-String _addColor(Object text, String colorCode) {
+String _addColor(String text, String colorCode) {
   return colorCode +
       text
           .toString()
@@ -603,7 +602,7 @@
   /// is enabled.
   ///
   /// Always prints to stdout.
-  void error(error, [stackTrace]) {
+  void error(Object error, [StackTrace? stackTrace]) {
     var errorJson = {'error': error.toString()};
 
     if (stackTrace == null && error is Error) stackTrace = error.stackTrace;
diff --git a/lib/src/oauth2.dart b/lib/src/oauth2.dart
index d4053f8..d4d88c9 100644
--- a/lib/src/oauth2.dart
+++ b/lib/src/oauth2.dart
@@ -135,7 +135,8 @@
       _clearCredentials();
       return withClient(fn);
     } else {
-      throw error;
+      // ignore: only_throw_errors
+      throw error as Object;
     }
   });
 }
@@ -226,7 +227,7 @@
   // Spin up a one-shot HTTP server to receive the authorization code from the
   // Google OAuth2 server via redirect. This server will close itself as soon as
   // the code is received.
-  var completer = Completer();
+  var completer = Completer<Client>();
   var server = await bindServer('localhost', 0);
   shelf_io.serveRequests(server, (request) {
     if (request.url.path.isNotEmpty) {
diff --git a/lib/src/package_config.dart b/lib/src/package_config.dart
index b215555..2123a4d 100644
--- a/lib/src/package_config.dart
+++ b/lib/src/package_config.dart
@@ -56,13 +56,13 @@
   ///
   /// Throws [FormatException], if format is invalid, this does not validate the
   /// contents only that the format is correct.
-  factory PackageConfig.fromJson(Object data) {
+  factory PackageConfig.fromJson(Object? data) {
     if (data is! Map<String, dynamic>) {
       throw FormatException('package_config.json must be a JSON object');
     }
     final root = data;
 
-    void throwFormatException(String property, String mustBe) =>
+    Never throwFormatException(String property, String mustBe) =>
         throw FormatException(
           '"$property" in .dart_tool/package_config.json $mustBe',
         );
@@ -85,7 +85,7 @@
     }
     final packages = <PackageConfigEntry>[];
     for (final entry in packagesRaw) {
-      packages.add(PackageConfigEntry.fromJson(entry));
+      packages.add(PackageConfigEntry.fromJson(entry as Object));
     }
 
     // Read the 'generated' property
@@ -100,7 +100,7 @@
 
     // Read the 'generator' property
     final generator = root['generator'];
-    if (generator != null && generator is! String) {
+    if (generator is! String?) {
       throw FormatException(
         '"generator" in package_config.json must be a string, if given',
       );
@@ -124,7 +124,7 @@
     }
 
     return PackageConfig(
-      configVersion: configVersion as int,
+      configVersion: configVersion,
       packages: packages,
       generated: generated,
       generator: generator,
diff --git a/lib/src/pub_embeddable_command.dart b/lib/src/pub_embeddable_command.dart
index 48c4258..06bba9e 100644
--- a/lib/src/pub_embeddable_command.dart
+++ b/lib/src/pub_embeddable_command.dart
@@ -24,6 +24,7 @@
 import 'command/uploader.dart';
 import 'log.dart' as log;
 import 'log.dart';
+import 'utils.dart';
 
 /// The information needed for the embedded pub command to send analytics.
 @sealed
@@ -56,7 +57,7 @@
   String get docUrl => 'https://dart.dev/tools/pub/cmd/pub-global';
 
   @override
-  String get directory => argResults['directory'];
+  String get directory => argResults.option('directory');
 
   @override
   final PubAnalytics? analytics;
@@ -120,6 +121,6 @@
   bool get trace => _isVerbose;
 
   bool get _isVerbose {
-    return argResults['verbose'] || isVerbose();
+    return argResults.flag('verbose') || isVerbose();
   }
 }
diff --git a/lib/src/pubspec.dart b/lib/src/pubspec.dart
index c27fdb7..1b1e3b8 100644
--- a/lib/src/pubspec.dart
+++ b/lib/src/pubspec.dart
@@ -102,10 +102,11 @@
     final pubspecOverridesFields = _overridesFileFields;
     if (pubspecOverridesFields != null) {
       pubspecOverridesFields.nodes.forEach((key, _) {
-        if (!const {'dependency_overrides'}.contains(key.value)) {
+        final keyNode = key as YamlNode;
+        if (!const {'dependency_overrides'}.contains(keyNode.value)) {
           throw SourceSpanApplicationException(
             'pubspec_overrides.yaml only supports the `dependency_overrides` field.',
-            key.span,
+            keyNode.span,
           );
         }
       });
@@ -175,6 +176,7 @@
 
     if (yaml is YamlMap) {
       yaml.nodes.forEach((nameNode, constraintNode) {
+        if (nameNode is! YamlNode) throw AssertionError('Bad state');
         final name = nameNode.value;
         if (name is! String) {
           _error('SDK names must be strings.', nameNode.span);
@@ -254,13 +256,13 @@
     this.dependencyOverridesFromOverridesFile = false,
   })  : _dependencies = dependencies == null
             ? null
-            : Map.fromIterable(dependencies, key: (range) => range.name),
+            : {for (final d in dependencies) d.name: d},
         _devDependencies = devDependencies == null
             ? null
-            : Map.fromIterable(devDependencies, key: (range) => range.name),
+            : {for (final d in devDependencies) d.name: d},
         _dependencyOverrides = dependencyOverrides == null
             ? null
-            : Map.fromIterable(dependencyOverrides, key: (range) => range.name),
+            : {for (final d in dependencyOverrides) d.name: d},
         _givenSdkConstraints = sdkConstraints ??
             UnmodifiableMapView({'dart': SdkConstraint(VersionConstraint.any)}),
         _includeDefaultSdkConstraint = false,
@@ -426,61 +428,63 @@
     _error('"$field" field must be a map.', node.span);
   }
 
-  var nonStringNode =
-      node.nodes.keys.firstWhere((e) => e.value is! String, orElse: () => null);
+  var nonStringNode = node.nodes.keys
+      .firstWhereOrNull((e) => e is YamlScalar && e.value is! String);
   if (nonStringNode != null) {
-    _error('A dependency name must be a string.', nonStringNode.span);
+    _error(
+      'A dependency name must be a string.',
+      (nonStringNode as YamlNode).span,
+    );
   }
 
   node.nodes.forEach(
     (nameNode, specNode) {
-      var name = nameNode.value;
+      var name = (nameNode as YamlNode).value;
+      if (name is! String) {
+        _error('A dependency name must be a string.', nameNode.span);
+      }
       var spec = specNode.value;
       if (packageName != null && name == packageName) {
         _error('A package may not list itself as a dependency.', nameNode.span);
       }
 
-      YamlNode? descriptionNode;
-      String? sourceName;
-
+      final String? sourceName;
       VersionConstraint versionConstraint = VersionRange();
+      YamlNode? descriptionNode;
       if (spec == null) {
         sourceName = null;
       } else if (spec is String) {
         sourceName = null;
         versionConstraint =
             _parseVersionConstraint(specNode, packageName, fileType);
-      } else if (spec is Map) {
+      } else if (specNode is YamlMap) {
         // Don't write to the immutable YAML map.
-        spec = Map.from(spec);
-        var specMap = specNode as YamlMap;
-
-        if (spec.containsKey('version')) {
-          spec.remove('version');
-          versionConstraint = _parseVersionConstraint(
-            specMap.nodes['version'],
-            packageName,
-            fileType,
-          );
-        }
-
-        var sourceNames = spec.keys.toList();
-        if (sourceNames.length > 1) {
+        final versionNode = specNode.nodes['version'];
+        versionConstraint = _parseVersionConstraint(
+          versionNode,
+          packageName,
+          fileType,
+        );
+        final otherEntries = specNode.nodes.entries
+            .where((entry) => entry.key.value != 'version')
+            .toList();
+        if (otherEntries.length > 1) {
           _error('A dependency may only have one source.', specNode.span);
-        } else if (sourceNames.isEmpty) {
+        } else if (otherEntries.isEmpty) {
           // Default to a hosted dependency if no source is specified.
           sourceName = 'hosted';
+        } else {
+          switch (otherEntries.single) {
+            case MapEntry(key: YamlScalar(value: String s), value: final d):
+              sourceName = s;
+              descriptionNode = d;
+            case MapEntry(key: final k, value: _):
+              _error(
+                'A source name must be a string.',
+                (k as YamlNode).span,
+              );
+          }
         }
-
-        sourceName ??= sourceNames.single;
-        if (sourceName is! String) {
-          _error(
-            'A source name must be a string.',
-            specMap.nodes.keys.single.span,
-          );
-        }
-
-        descriptionNode ??= specMap.nodes[sourceName];
       } else {
         _error(
           'A dependency specification must be a string or a mapping.',
@@ -536,7 +540,8 @@
   if (node?.value == null) {
     return VersionConstraint.any;
   }
-  if (node!.value is! String) {
+  final value = node!.value;
+  if (value is! String) {
     _error('A version constraint must be a string.', node.span);
   }
 
@@ -544,7 +549,7 @@
     'version constraint',
     node.span,
     () {
-      var constraint = VersionConstraint.parse(node.value);
+      var constraint = VersionConstraint.parse(value);
       return constraint;
     },
     packageName,
diff --git a/lib/src/pubspec_parse.dart b/lib/src/pubspec_parse.dart
index df9fd3a..d4d6acb 100644
--- a/lib/src/pubspec_parse.dart
+++ b/lib/src/pubspec_parse.dart
@@ -131,7 +131,7 @@
     }
 
     _parsedPublishTo = true;
-    _publishTo = publishTo;
+    _publishTo = publishTo as String?;
     return _publishTo;
   }
 
@@ -161,7 +161,7 @@
             if (value is! String) {
               falseSecretsError(node.span);
             }
-            falseSecrets.add(value);
+            falseSecrets.add(value as String);
           }
         } else {
           falseSecretsError(falseSecretsNode.span);
@@ -198,13 +198,17 @@
       );
     }
 
-    yaml.nodes.forEach((key, value) {
-      if (key.value is! String) {
+    var yamlMap = yaml;
+
+    yamlMap.nodes.forEach((key, value) {
+      key = key as YamlNode;
+      final keyValue = key.value;
+      if (keyValue is! String) {
         _error('"executables" keys must be strings.', key.span);
       }
 
       final keyPattern = RegExp(r'^[a-zA-Z0-9_-]+$');
-      if (!keyPattern.hasMatch(key.value)) {
+      if (!keyPattern.hasMatch(keyValue)) {
         _error(
           '"executables" keys may only contain letters, '
           'numbers, hyphens and underscores.',
@@ -212,21 +216,16 @@
         );
       }
 
-      if (value.value == null) {
-        value = key;
-      } else if (value.value is! String) {
-        _error('"executables" values must be strings or null.', value.span);
-      }
-
       final valuePattern = RegExp(r'[/\\]');
-      if (valuePattern.hasMatch(value.value)) {
-        _error(
-          '"executables" values may not contain path separators.',
-          value.span,
-        );
-      }
-
-      _executables![key.value] = value.value;
+      _executables![keyValue] = switch (value.value) {
+        null => keyValue,
+        String s when valuePattern.hasMatch(s) => _error(
+            '"executables" values may not contain path separators.',
+            value.span,
+          ),
+        String s => s,
+        _ => _error('"executables" values must be strings or null.', value.span)
+      };
     });
 
     return _executables!;
@@ -267,7 +266,7 @@
   }
 
   /// Throws a [SourceSpanApplicationException] with the given message.
-  void _error(String message, SourceSpan? span) {
+  Never _error(String message, SourceSpan? span) {
     throw SourceSpanApplicationException(message, span);
   }
 }
diff --git a/lib/src/solver/package_lister.dart b/lib/src/solver/package_lister.dart
index ce794b7..45ec5b8 100644
--- a/lib/src/solver/package_lister.dart
+++ b/lib/src/solver/package_lister.dart
@@ -275,7 +275,8 @@
     var index = lowerBound(
       versions,
       id,
-      compare: (dynamic id1, dynamic id2) => id1.version.compareTo(id2.version),
+      compare: (PackageId id1, PackageId id2) =>
+          id1.version.compareTo(id2.version),
     );
     assert(index < versions.length);
     assert(versions[index].version == id.version);
diff --git a/lib/src/source/git.dart b/lib/src/source/git.dart
index 36401d6..32ef867 100644
--- a/lib/src/source/git.dart
+++ b/lib/src/source/git.dart
@@ -95,7 +95,7 @@
     }
 
     var ref = description['ref'];
-    if (ref != null && ref is! String) {
+    if (ref is! String?) {
       throw FormatException("The 'ref' field of the description must be a "
           'string.');
     }
@@ -107,13 +107,17 @@
     }
 
     final url = description['url'];
+    if (url is! String) {
+      throw FormatException("The 'url' field of the description "
+          'must be a string.');
+    }
     return PackageId(
       name,
       version,
       GitResolvedDescription(
         GitDescription(
           url: url,
-          ref: ref ?? 'HEAD',
+          ref: ref,
           path: _validatedPath(
             description['path'],
           ),
@@ -465,7 +469,7 @@
       } on git.GitException catch (error, stackTrace) {
         log.error('Failed to reset ${log.bold(package.name)} '
             '${package.version}. Error:\n$error');
-        log.fine(stackTrace);
+        log.fine(stackTrace.toString());
         result.add(
           RepairResult(package.name, package.version, this, success: false),
         );
diff --git a/lib/src/source/hosted.dart b/lib/src/source/hosted.dart
index 1b28401..2d7376a 100644
--- a/lib/src/source/hosted.dart
+++ b/lib/src/source/hosted.dart
@@ -279,7 +279,7 @@
       version,
       ResolvedHostedDescription(
         HostedDescription(name, url),
-        sha256: _parseContentHash(sha256),
+        sha256: _parseContentHash(sha256 as String?),
       ),
     );
   }
@@ -362,7 +362,7 @@
     }
     final url = u ?? defaultUrl;
 
-    return HostedDescription(name, url);
+    return HostedDescription(name, url as String);
   }
 
   static final RegExp _looksLikePackageName =
@@ -407,17 +407,29 @@
       if (archiveUrl is! String) {
         throw FormatException('archive_url must be a String');
       }
+      final isDiscontinued = body['isDiscontinued'] ?? false;
+      if (isDiscontinued is! bool) {
+        throw FormatException('isDiscontinued must be a bool');
+      }
+      final replacedBy = body['replacedBy'];
+      if (replacedBy is! String?) {
+        throw FormatException('replacedBy must be a String');
+      }
+      final retracted = map['retracted'] ?? false;
+      if (retracted is! bool) {
+        throw FormatException('retracted must be a bool');
+      }
       final status = PackageStatus(
-        isDiscontinued: body['isDiscontinued'] ?? false,
-        discontinuedReplacedBy: body['replacedBy'],
-        isRetracted: map['retracted'] ?? false,
+        isDiscontinued: isDiscontinued,
+        discontinuedReplacedBy: replacedBy,
+        isRetracted: retracted,
       );
       return _VersionInfo(
         pubspec.version,
         pubspec,
         Uri.parse(archiveUrl),
         status,
-        _parseContentHash(archiveSha256),
+        _parseContentHash(archiveSha256 as String?),
       );
     }).toList();
   }
@@ -458,7 +470,12 @@
         throw FormatException('version listing must be a mapping');
       }
       body = decoded;
-      result = _versionInfoFromPackageListing(body, ref, url, cache);
+      result = _versionInfoFromPackageListing(
+        body as Map<String, dynamic>,
+        ref,
+        url,
+        cache,
+      );
     } on Exception catch (error, stackTrace) {
       _throwFriendlyError(error, stackTrace, packageName, hostedUrl);
     }
@@ -982,7 +999,7 @@
                       '${package.version}';
                   if (url != defaultUrl) message += ' from $url';
                   log.error('$message. Error:\n$error');
-                  log.fine(stackTrace);
+                  log.fine(stackTrace.toString());
 
                   tryDeleteEntry(package.dir);
                   return RepairResult(
diff --git a/lib/src/source/path.dart b/lib/src/source/path.dart
index 83358ea..8db723e 100644
--- a/lib/src/source/path.dart
+++ b/lib/src/source/path.dart
@@ -117,7 +117,7 @@
       }
 
       path = p.normalize(
-        p.absolute(p.join(containingDir, description['path'])),
+        p.absolute(p.join(containingDir, path)),
       );
     }
 
diff --git a/lib/src/system_cache.dart b/lib/src/system_cache.dart
index 971adba..ff2b535 100644
--- a/lib/src/system_cache.dart
+++ b/lib/src/system_cache.dart
@@ -67,8 +67,10 @@
   })();
 
   /// The available sources.
-  late final _sources =
-      Map.fromIterable([hosted, git, path, sdk], key: (source) => source.name);
+  late final _sources = Map<String, Source>.fromIterable(
+    [hosted, git, path, sdk],
+    key: (source) => (source as Source).name,
+  );
 
   Source sources(String? name) {
     return name == null
diff --git a/lib/src/utils.dart b/lib/src/utils.dart
index 061bd7c..4f144e4 100644
--- a/lib/src/utils.dart
+++ b/lib/src/utils.dart
@@ -9,6 +9,7 @@
 import 'dart:math' as math;
 import 'dart:typed_data';
 
+import 'package:args/args.dart';
 import 'package:convert/convert.dart';
 import 'package:crypto/crypto.dart' as crypto;
 import 'package:pub_semver/pub_semver.dart';
@@ -110,7 +111,9 @@
 }) {
   var completer = Completer<T>();
   void wrappedCallback() {
-    Future.sync(callback).then(completer.complete).catchError((e, stackTrace) {
+    Future.sync(callback)
+        .then(completer.complete)
+        .catchError((Object e, StackTrace? stackTrace) {
       // [stackTrace] can be null if we're running without [captureStackChains],
       // since dart:io will often throw errors without stack traces.
       if (stackTrace != null) {
@@ -118,7 +121,9 @@
       } else {
         stackTrace = Chain([]);
       }
-      if (!completer.isCompleted) completer.completeError(e, stackTrace);
+      if (!completer.isCompleted) {
+        completer.completeError(e, stackTrace);
+      }
     });
   }
 
@@ -147,12 +152,13 @@
 Future<List<T>> waitAndPrintErrors<T>(Iterable<Future<T>> futures) {
   return Future.wait(
     futures.map((future) {
-      return future.catchError((error, stackTrace) {
+      return future.catchError((Object error, StackTrace? stackTrace) {
         log.exception(error, stackTrace);
+        // ignore: only_throw_errors
         throw error;
       });
     }),
-  ).catchError((error, stackTrace) {
+  ).catchError((Object error, StackTrace? stackTrace) {
     throw SilentException(error, stackTrace);
   });
 }
@@ -287,7 +293,7 @@
 ) async {
   int? minIndex;
   T? minOrderBy;
-  List valuesList = values.toList();
+  var valuesList = values.toList();
   final orderByResults = await Future.wait(values.map(orderBy));
   for (var i = 0; i < orderByResults.length; i++) {
     final elementOrderBy = orderByResults[i];
@@ -735,3 +741,10 @@
     await Future.delayed(delay < maxDelay ? delay : maxDelay);
   }
 }
+
+extension RetrieveFlags on ArgResults {
+  bool flag(String name) => this[name] as bool;
+
+  String option(String name) => this[name] as String;
+  String? optionWithoutDefault(String name) => this[name] as String?;
+}
diff --git a/lib/src/validator/pubspec_typo.dart b/lib/src/validator/pubspec_typo.dart
index c51c678..0c2ac9a 100644
--- a/lib/src/validator/pubspec_typo.dart
+++ b/lib/src/validator/pubspec_typo.dart
@@ -19,6 +19,7 @@
       if (_validPubspecKeys.contains(key)) {
         continue;
       }
+      if (key is! String) continue;
 
       var bestLevenshteinRatio = 100.0;
       var closestKey = '';
diff --git a/lib/src/validator/strict_dependencies.dart b/lib/src/validator/strict_dependencies.dart
index 728f592..50fd208 100644
--- a/lib/src/validator/strict_dependencies.dart
+++ b/lib/src/validator/strict_dependencies.dart
@@ -35,7 +35,7 @@
       } on AnalyzerErrorGroup catch (e, s) {
         // Ignore files that do not parse.
         log.fine(getErrorMessage(e));
-        log.fine(Chain.forTrace(s).terse);
+        log.fine(Chain.forTrace(s).terse.toString());
         continue;
       }
 
diff --git a/test/cache/list_test.dart b/test/cache/list_test.dart
index c5fd2fb..72f9339 100644
--- a/test/cache/list_test.dart
+++ b/test/cache/list_test.dart
@@ -9,7 +9,7 @@
 import '../test_pub.dart';
 
 void main() {
-  String hostedDir(package) {
+  String hostedDir(String package) {
     return path.join(d.sandbox, cachePath, 'hosted', 'pub.dev', package);
   }
 
diff --git a/test/dependency_services/dependency_services_test.dart b/test/dependency_services/dependency_services_test.dart
index 412be30..4dbef36 100644
--- a/test/dependency_services/dependency_services_test.dart
+++ b/test/dependency_services/dependency_services_test.dart
@@ -10,6 +10,7 @@
 import 'package:pub_semver/pub_semver.dart';
 import 'package:shelf/shelf.dart' as shelf;
 import 'package:test/test.dart';
+import 'package:yaml/yaml.dart';
 import 'package:yaml_edit/yaml_edit.dart';
 
 import '../descriptor.dart' as d;
@@ -102,7 +103,7 @@
   await context.runDependencyServices(['list']);
   final report = await context.runDependencyServices(['report']);
   if (reportAssertions != null) {
-    reportAssertions(json.decode(report));
+    reportAssertions(json.decode(report) as Map);
   }
   final input = json.encode({
     'dependencyChanges': upgrades,
@@ -120,7 +121,7 @@
       '--snapshot=$snapshot',
       p.join('bin', 'dependency_services.dart'),
     ]);
-    expect(r.exitCode, 0, reason: r.stderr);
+    expect(r.exitCode, 0, reason: r.stderr as String);
   });
 
   tearDownAll(() {
@@ -254,7 +255,7 @@
     final lockFileYaml = YamlEditor(
       lockFile.readAsStringSync(),
     );
-    for (final p in lockFileYaml.parseAt(['packages']).value.entries) {
+    for (final p in (lockFileYaml.parseAt(['packages']) as YamlMap).entries) {
       lockFileYaml.remove(['packages', p.key, 'description', 'sha256']);
     }
     lockFile.writeAsStringSync(lockFileYaml.toString());
@@ -289,7 +290,8 @@
     final lockFileYaml = YamlEditor(
       lockFile.readAsStringSync(),
     );
-    for (final p in lockFileYaml.parseAt(['packages']).value.entries) {
+    for (final p
+        in lockFileYaml.parseAt(['packages']).value.entries as Iterable) {
       lockFileYaml.update(
         ['packages', p.key, 'description', 'url'],
         'https://pub.dartlang.org',
diff --git a/test/descriptor.dart b/test/descriptor.dart
index d41001e..38f9161 100644
--- a/test/descriptor.dart
+++ b/test/descriptor.dart
@@ -175,14 +175,18 @@
 /// validated since they will often lack the dependencies section that the
 /// real pubspec being compared against has. You usually only need to pass
 /// `true` for this if you plan to call [create] on the resulting descriptor.
-Descriptor cacheDir(Map packages, {int? port, bool includePubspecs = false}) {
+Descriptor cacheDir(
+  Map<String, dynamic> packages, {
+  int? port,
+  bool includePubspecs = false,
+}) {
   var contents = <Descriptor>[];
   packages.forEach((name, versions) {
     if (versions is! List) versions = [versions];
     for (var version in versions) {
       var packageContents = [libDir(name, '$name $version')];
       if (includePubspecs) {
-        packageContents.add(libPubspec(name, version));
+        packageContents.add(libPubspec(name, version as String));
       }
       contents.add(dir('$name-$version', packageContents));
     }
diff --git a/test/descriptor/package_config.dart b/test/descriptor/package_config.dart
index b508dc6..2b644c2 100644
--- a/test/descriptor/package_config.dart
+++ b/test/descriptor/package_config.dart
@@ -52,9 +52,7 @@
       fail("File not found: '$packageConfigFile'.");
     }
 
-    Map<String, dynamic> rawJson = json.decode(
-      await File(packageConfigFile).readAsString(),
-    );
+    final rawJson = json.decode(await File(packageConfigFile).readAsString());
     PackageConfig config;
     try {
       config = PackageConfig.fromJson(rawJson);
diff --git a/test/embedding/embedding_test.dart b/test/embedding/embedding_test.dart
index cf504d6..6ab32ad 100644
--- a/test/embedding/embedding_test.dart
+++ b/test/embedding/embedding_test.dart
@@ -92,7 +92,7 @@
       Platform.resolvedExecutable,
       ['--snapshot=$snapshot', _commandRunner],
     );
-    expect(r.exitCode, 0, reason: r.stderr);
+    expect(r.exitCode, 0, reason: r.stderr as String);
   });
 
   tearDownAll(() {
diff --git a/test/embedding/get_executable_for_command_test.dart b/test/embedding/get_executable_for_command_test.dart
index 8781897..f56c57c 100644
--- a/test/embedding/get_executable_for_command_test.dart
+++ b/test/embedding/get_executable_for_command_test.dart
@@ -17,7 +17,7 @@
 Future<void> testGetExecutable(
   String command,
   String root, {
-  allowSnapshot = true,
+  bool allowSnapshot = true,
   executable,
   packageConfig,
   errorMessage,
diff --git a/test/lish/many_files_test.dart b/test/lish/many_files_test.dart
index 50a45f9..dd54737 100644
--- a/test/lish/many_files_test.dart
+++ b/test/lish/many_files_test.dart
@@ -79,7 +79,7 @@
             '${result.stderr}');
       }
 
-      argMax = int.parse(result.stdout);
+      argMax = int.parse(result.stdout as String);
     }
 
     var appRoot = p.join(d.sandbox, appPath);
diff --git a/test/package_server.dart b/test/package_server.dart
index a2dd4f5..b5d07ba 100644
--- a/test/package_server.dart
+++ b/test/package_server.dart
@@ -342,7 +342,7 @@
   // Overrides the calculated sha256.
   String? sha256;
 
-  Version get version => Version.parse(pubspec['version']);
+  Version get version => Version.parse(pubspec['version'] as String);
 
   _ServedPackageVersion(this.pubspec, {required this.contents, this.headers});
 
diff --git a/test/rate_limited_scheduler_test.dart b/test/rate_limited_scheduler_test.dart
index 6c94751..3a387c3 100644
--- a/test/rate_limited_scheduler_test.dart
+++ b/test/rate_limited_scheduler_test.dart
@@ -181,7 +181,7 @@
     Future<String?> f(String i) async {
       isBeingProcessed[i]!.complete();
       await completers[i]!.future;
-      return Zone.current['zoneValue'];
+      return Zone.current['zoneValue'] as String?;
     }
 
     final scheduler = RateLimitedScheduler(f, maxConcurrentOperations: 2);
diff --git a/test/test_pub.dart b/test/test_pub.dart
index 8f84125..f79ac4b 100644
--- a/test/test_pub.dart
+++ b/test/test_pub.dart
@@ -149,7 +149,7 @@
   int? exitCode,
   Map<String, String?>? environment,
   String? workingDirectory,
-  includeParentHomeAndPath = true,
+  bool includeParentHomeAndPath = true,
 }) async {
   if (error != null && warning != null) {
     throw ArgumentError("Cannot pass both 'error' and 'warning'.");
@@ -348,7 +348,7 @@
   String? workingDirectory,
   Map<String, String?>? environment,
   List<String>? input,
-  includeParentHomeAndPath = true,
+  bool includeParentHomeAndPath = true,
 }) async {
   exitCode ??= exit_codes.SUCCESS;
   // Cannot pass both output and outputJson.
@@ -486,7 +486,7 @@
   String? workingDirectory,
   Map<String, String?>? environment,
   bool verbose = true,
-  includeParentHomeAndPath = true,
+  bool includeParentHomeAndPath = true,
 }) async {
   args ??= [];
 
@@ -596,8 +596,8 @@
 
   /// This is protected.
   PubProcess(
-    process,
-    description, {
+    Process process,
+    String description, {
     Encoding encoding = utf8,
     bool forwardStdio = false,
   }) : super(
@@ -710,7 +710,7 @@
   Iterable<String>? sandbox,
   Map<String, String>? hosted,
 }) {
-  var dependencies = {};
+  var dependencies = <String, dynamic>{};
 
   if (sandbox != null) {
     for (var package in sandbox) {
@@ -866,15 +866,15 @@
 void _validateOutputJson(
   List<String> failures,
   String pipe,
-  expected,
+  Object? expected,
   String actualText,
 ) {
   late Map actual;
   try {
-    actual = jsonDecode(actualText);
+    actual = jsonDecode(actualText) as Map;
   } on FormatException {
     failures.add('Expected $pipe JSON:');
-    failures.add(expected);
+    failures.add(expected.toString());
     failures.add('Got invalid JSON:');
     failures.add(actualText);
   }
@@ -884,7 +884,7 @@
   actual['log']?.removeWhere(
     (entry) =>
         entry['level'] == 'Fine' &&
-        entry['message'].startsWith('Not yet complete after'),
+        (entry['message'] as String).startsWith('Not yet complete after'),
   );
 
   // Match against the expectation.
diff --git a/test/utils_test.dart b/test/utils_test.dart
index 9d71e31..62d5576 100644
--- a/test/utils_test.dart
+++ b/test/utils_test.dart
@@ -142,8 +142,9 @@
   group('minByAsync', () {
     test('is stable', () async {
       {
-        final completers = <String, Completer>{};
-        Completer completer(k) => completers.putIfAbsent(k, Completer.new);
+        final completers = <String, Completer<String>>{};
+        Completer<String> completer(String k) =>
+            completers.putIfAbsent(k, Completer.new);
         Future<int> lengthWhenComplete(String s) async {
           await completer(s).future;
           return s.length;
@@ -153,15 +154,16 @@
           minByAsync(['aa', 'a', 'b', 'ccc'], lengthWhenComplete),
           completion('a'),
         );
-        completer('aa').complete();
-        completer('b').complete();
-        completer('a').complete();
-        completer('ccc').complete();
+        completer('aa').complete('');
+        completer('b').complete('');
+        completer('a').complete('');
+        completer('ccc').complete('');
         await w;
       }
       {
-        final completers = <String, Completer>{};
-        Completer completer(k) => completers.putIfAbsent(k, Completer.new);
+        final completers = <String, Completer<String>>{};
+        Completer<String> completer(String k) =>
+            completers.putIfAbsent(k, Completer.new);
         Future<int> lengthWhenComplete(String s) async {
           await completer(s).future;
           return s.length;
@@ -171,10 +173,10 @@
           minByAsync(['aa', 'a', 'b', 'ccc'], lengthWhenComplete),
           completion('a'),
         );
-        completer('ccc').complete();
-        completer('a').complete();
-        completer('b').complete();
-        completer('aa').complete();
+        completer('ccc').complete('');
+        completer('a').complete('');
+        completer('b').complete('');
+        completer('aa').complete('');
         await w;
       }
     });
diff --git a/test/version_solver_test.dart b/test/version_solver_test.dart
index df3ae2b..c5b89a5 100644
--- a/test/version_solver_test.dart
+++ b/test/version_solver_test.dart
@@ -1985,10 +1985,10 @@
       LockFile.load(p.join(d.sandbox, appPath, 'pubspec.lock'), registry);
   var resultPubspec = Pubspec.fromMap({'dependencies': result}, registry);
 
-  var ids = Map.from(lockFile.packages);
+  var ids = {...lockFile.packages};
   for (var dep in resultPubspec.dependencies.values) {
     expect(ids, contains(dep.name));
-    var id = ids.remove(dep.name);
+    var id = ids.remove(dep.name)!;
     final description = dep.description;
     if (description is HostedDescription &&
         (description.url == SystemCache().hosted.defaultUrl)) {
diff --git a/tool/extract_all_pub_dev.dart b/tool/extract_all_pub_dev.dart
index 5d69ab3..9301723 100644
--- a/tool/extract_all_pub_dev.dart
+++ b/tool/extract_all_pub_dev.dart
@@ -24,7 +24,7 @@
   request.attachMetadataHeaders();
   final response = await globalHttpClient.fetch(request);
   final result = json.decode(response.body);
-  return List<String>.from(result['packages']);
+  return List<String>.from(result['packages'] as List);
 }
 
 Future<List<String>> versionArchiveUrls(String packageName) async {
@@ -33,7 +33,9 @@
   request.attachMetadataHeaders();
   final response = await globalHttpClient.fetch(request);
   final result = json.decode(response.body);
-  return List<String>.from(result['versions'].map((v) => v['archive_url']));
+  return (result['versions'] as List)
+      .map((v) => v['archive_url'] as String)
+      .toList();
 }
 
 Future<void> main() async {
@@ -41,11 +43,11 @@
   var failures = <Map<String, dynamic>?>[];
   if (fileExists(statusFilename)) {
     final json = jsonDecode(readTextFile(statusFilename));
-    for (final packageName in json['packages'] ?? []) {
-      alreadyDonePackages.add(packageName);
+    for (final packageName in json['packages'] as Iterable? ?? []) {
+      alreadyDonePackages.add(packageName as String);
     }
-    for (final failure in json['failures'] ?? []) {
-      failures.add(failure);
+    for (final failure in (json['failures'] ?? []) as Iterable) {
+      failures.add(failure as Map<String, dynamic>);
     }
   }
   log.message('Already processed ${alreadyDonePackages.length} packages');
diff --git a/tool/test-bin/pub_command_runner.dart b/tool/test-bin/pub_command_runner.dart
index 9192079..36094d8 100644
--- a/tool/test-bin/pub_command_runner.dart
+++ b/tool/test-bin/pub_command_runner.dart
@@ -12,6 +12,7 @@
 import 'package:pub/src/command.dart';
 import 'package:pub/src/exit_codes.dart' as exit_codes;
 import 'package:pub/src/log.dart' as log;
+import 'package:pub/src/utils.dart';
 import 'package:usage/usage.dart';
 
 final Analytics loggingAnalytics = _LoggingAnalytics();
@@ -77,7 +78,7 @@
 }
 
 class Runner extends CommandRunner<int> {
-  late ArgResults _options;
+  late ArgResults _results;
 
   Runner() : super('pub_command_runner', 'Tests the embeddable pub command.') {
     final analytics = Platform.environment['_PUB_LOG_ANALYTICS'] == 'true'
@@ -87,7 +88,10 @@
           )
         : null;
     addCommand(
-      pubCommand(analytics: analytics, isVerbose: () => _options['verbose'])
+      pubCommand(
+        analytics: analytics,
+        isVerbose: () => _results.flag('verbose'),
+      )
         ..addSubcommand(ThrowingCommand())
         ..addSubcommand(EnsurePubspecResolvedCommand()),
     );
@@ -98,11 +102,11 @@
   @override
   Future<int> run(Iterable<String> args) async {
     try {
-      _options = super.parse(args);
-      if (_options['verbose']) {
+      _results = super.parse(args);
+      if (_results.flag('verbose')) {
         log.verbosity = log.Verbosity.all;
       }
-      return await runCommand(_options);
+      return await runCommand(_results);
     } on UsageException catch (error) {
       log.exception(error);
       return exit_codes.USAGE;