From 5a3f21b4db2969a66899eca2cc097cd460b7776d Mon Sep 17 00:00:00 2001 From: Brett Sutton Date: Thu, 1 Jun 2023 19:32:07 +1000 Subject: [PATCH] Add strict-cast analysis option (#3919) --- .gitignore | 1 + analysis_options.yaml | 3 + bin/dependency_services.dart | 7 +- bin/pub.dart | 0 lib/src/command.dart | 2 +- lib/src/command/add.dart | 20 ++--- lib/src/command/cache_add.dart | 7 +- lib/src/command/cache_clean.dart | 3 +- lib/src/command/cache_repair.dart | 10 ++- lib/src/command/dependency_services.dart | 28 +++--- lib/src/command/deps.dart | 8 +- lib/src/command/downgrade.dart | 7 +- lib/src/command/get.dart | 17 ++-- lib/src/command/global_activate.dart | 11 +-- lib/src/command/global_run.dart | 3 +- lib/src/command/lish.dart | 10 +-- lib/src/command/login.dart | 8 +- lib/src/command/outdated.dart | 39 +++++---- lib/src/command/remove.dart | 11 +-- lib/src/command/run.dart | 3 +- lib/src/command/token_add.dart | 2 +- lib/src/command/token_list.dart | 2 +- lib/src/command/token_remove.dart | 3 +- lib/src/command/upgrade.dart | 14 +-- lib/src/command/uploader.dart | 2 +- lib/src/command_runner.dart | 15 ++-- lib/src/entrypoint.dart | 2 +- lib/src/error_group.dart | 9 +- lib/src/global_packages.dart | 2 +- lib/src/http.dart | 22 ++--- lib/src/io.dart | 16 ++-- lib/src/lock_file.dart | 12 +-- lib/src/log.dart | 37 ++++---- lib/src/oauth2.dart | 5 +- lib/src/package_config.dart | 10 +-- lib/src/pub_embeddable_command.dart | 5 +- lib/src/pubspec.dart | 85 ++++++++++--------- lib/src/pubspec_parse.dart | 39 +++++---- lib/src/solver/package_lister.dart | 3 +- lib/src/source/git.dart | 10 ++- lib/src/source/hosted.dart | 33 +++++-- lib/src/source/path.dart | 2 +- lib/src/system_cache.dart | 6 +- lib/src/utils.dart | 23 +++-- lib/src/validator/pubspec_typo.dart | 1 + lib/src/validator/strict_dependencies.dart | 2 +- test/cache/list_test.dart | 2 +- .../dependency_services_test.dart | 10 ++- test/descriptor.dart | 8 +- test/descriptor/package_config.dart | 4 +- test/embedding/embedding_test.dart | 2 +- .../get_executable_for_command_test.dart | 2 +- test/lish/many_files_test.dart | 2 +- test/package_server.dart | 2 +- test/rate_limited_scheduler_test.dart | 2 +- test/test_pub.dart | 20 ++--- test/utils_test.dart | 26 +++--- test/version_solver_test.dart | 4 +- tool/extract_all_pub_dev.dart | 14 +-- tool/test-bin/pub_command_runner.dart | 14 +-- 60 files changed, 380 insertions(+), 292 deletions(-) mode change 100644 => 100755 bin/pub.dart diff --git a/.gitignore b/.gitignore index 89fbed2b8..d35929858 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 85fbba91e..4c23fc5d3 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -9,6 +9,9 @@ analyzer: 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 e9d76acc7..9fd589dca 100644 --- a/bin/dependency_services.dart +++ b/bin/dependency_services.dart @@ -16,17 +16,18 @@ import 'package:pub/src/command/dependency_services.dart'; 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 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 b7ee19153..29945020c 100644 --- a/lib/src/command.dart +++ b/lib/src/command.dart @@ -68,7 +68,7 @@ abstract class PubCommand extends Command { 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 06a0260c2..b8a23cdf6 100644 --- a/lib/src/command/add.dart +++ b/lib/src/command/add.dart @@ -766,21 +766,21 @@ class _ParseResult { } 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 85c2c5d71..25508ec7d 100644 --- a/lib/src/command/cache_add.dart +++ b/lib/src/command/cache_add.dart @@ -8,6 +8,7 @@ import 'package:pub_semver/pub_semver.dart'; 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 @@ class CacheAddCommand extends PubCommand { 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 @@ class CacheAddCommand extends PubCommand { fail('Package $package has no versions that match $constraint.'); } - Future downloadVersion(id) async { + Future 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 786707df9..d981b1f85 100644 --- a/lib/src/command/cache_clean.dart +++ b/lib/src/command/cache_clean.dart @@ -6,6 +6,7 @@ import '../command.dart'; import '../command_runner.dart'; import '../io.dart'; import '../log.dart' as log; +import '../utils.dart'; class CacheCleanCommand extends PubCommand { @override @@ -27,7 +28,7 @@ class CacheCleanCommand extends PubCommand { @override Future 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 1c3cd8c56..b0e0b042d 100644 --- a/lib/src/command/cache_repair.dart +++ b/lib/src/command/cache_repair.dart @@ -38,13 +38,15 @@ class CacheRepairCommand extends PubCommand { 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 @@ class CacheRepairCommand extends PubCommand { 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 ced8c002b..44ae700d8 100644 --- a/lib/src/command/dependency_services.dart +++ b/lib/src/command/dependency_services.dart @@ -69,8 +69,10 @@ class DependencyServicesReportCommand extends PubCommand { } else { final resolution = await _tryResolve(entrypoint.root.pubspec, cache) ?? (throw DataException('Failed to resolve pubspec')); - currentPackages = - Map.fromIterable(resolution, key: (e) => e.name); + currentPackages = Map.fromIterable( + resolution, + key: (e) => (e as PackageId).name, + ); } currentPackages.remove(entrypoint.root.name); @@ -361,13 +363,13 @@ class DependencyServicesApplyCommand extends PubCommand { 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 @@ class DependencyServicesApplyCommand extends PubCommand { } 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 @@ class DependencyServicesApplyCommand extends PubCommand { ); } 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 0fe86c25a..a3e2be82b 100644 --- a/lib/src/command/deps.dart +++ b/lib/src/command/deps.dart @@ -32,7 +32,7 @@ class DepsCommand extends PubCommand { 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 @@ class DepsCommand extends PubCommand { 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 @@ class DepsCommand extends PubCommand { ), ); } else { - if (argResults['executables']) { + if (argResults.flag('executables')) { _outputExecutables(buffer); } else { for (var sdk in sdks.values) { @@ -174,7 +174,7 @@ class DepsCommand extends PubCommand { } } - 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 e92b89462..8c7a17d09 100644 --- a/lib/src/command/downgrade.dart +++ b/lib/src/command/downgrade.dart @@ -7,6 +7,7 @@ import 'dart:async'; 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 @@ class DowngradeCommand extends PubCommand { 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 @@ class DowngradeCommand extends PubCommand { ), ); } - var dryRun = argResults['dry-run']; + var dryRun = argResults.flag('dry-run'); await entrypoint.acquireDependencies( SolveType.downgrade, @@ -71,7 +72,7 @@ class DowngradeCommand extends PubCommand { 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 51419c139..254428281 100644 --- a/lib/src/command/get.dart +++ b/lib/src/command/get.dart @@ -7,6 +7,7 @@ import 'dart:async'; 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 @@ class GetCommand extends PubCommand { @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 @@ class GetCommand extends PubCommand { 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 8fdbc4649..2080894c4 100644 --- a/lib/src/command/global_activate.dart +++ b/lib/src/command/global_activate.dart @@ -87,8 +87,8 @@ class GlobalActivateCommand extends PubCommand { usageException('Cannot pass both --no-executables and --executable.'); } - executables = argResults['executable']; - } else if (argResults['no-executables']) { + executables = argResults['executable'] as List?; + } else if (argResults.flag('no-executables')) { // An empty list means no executables. executables = []; } @@ -126,8 +126,8 @@ class GlobalActivateCommand extends PubCommand { 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 @@ class GlobalActivateCommand extends PubCommand { 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 c7e615b16..c6e996f41 100644 --- a/lib/src/command/global_run.dart +++ b/lib/src/command/global_run.dart @@ -79,7 +79,8 @@ class GlobalRunCommand extends PubCommand { 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 294df13ae..9c4372c72 100644 --- a/lib/src/command/lish.dart +++ b/lib/src/command/lish.dart @@ -40,7 +40,7 @@ class LishCommand extends PubCommand { // 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 @@ class LishCommand extends PubCommand { }(); /// 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 @@ class LishCommand extends PubCommand { 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 670d1a816..68ec850b7 100644 --- a/lib/src/command/login.dart +++ b/lib/src/command/login.dart @@ -47,11 +47,17 @@ class LoginCommand extends PubCommand { 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 dee80a19f..050cedc21 100644 --- a/lib/src/command/outdated.dart +++ b/lib/src/command/outdated.dart @@ -39,7 +39,8 @@ class OutdatedCommand extends PubCommand { /// 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 @@ Consider using the Dart 2.19 sdk to migrate to null safety.'''); } 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 @@ Consider using the Dart 2.19 sdk to migrate to null safety.'''); 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 @@ Consider using the Dart 2.19 sdk to migrate to null safety.'''); } bool get showTransitiveDependencies { - return argResults['transitive']; + return argResults.flag('transitive'); } late final bool prereleases = () { @@ -330,10 +332,10 @@ Consider using the Dart 2.19 sdk to migrate to null safety.'''); // '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 @@ Consider using the Dart 2.19 sdk to migrate to null safety.'''); Package root, Iterable resolution, ) async { - final nameToId = Map.fromIterable( - resolution, - key: (id) => id.name, - ); + final nameToId = {for (final id in resolution) id.name: id}; final nonDevDependencies = {root.name}; final queue = [...root.dependencies.keys]; @@ -865,7 +864,7 @@ enum _DependencyKind { _FormattedString _format( String value, String Function(String) format, { - prefix = '', + String? prefix = '', }) { return _FormattedString(value, format: format, prefix: prefix); } @@ -887,10 +886,10 @@ class _MarkedVersionDetails { _MarkedVersionDetails( this._versionDetails, { required this.asDesired, - format, - prefix = '', - suffix = '', - jsonExplanation, + String Function(String)? format, + String? prefix = '', + String? suffix = '', + MapEntry? jsonExplanation, }) : _format = format, _prefix = prefix, _suffix = suffix, @@ -927,8 +926,8 @@ class _FormattedString { _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 a367588f6..7063c52f7 100644 --- a/lib/src/command/remove.dart +++ b/lib/src/command/remove.dart @@ -10,6 +10,7 @@ import '../io.dart'; 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 @@ To remove a dependency override of a package prefix the package name with @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 @@ To remove a dependency override of a package prefix the package name with 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 67920e1ba..9fe1a0392 100644 --- a/lib/src/command/run.dart +++ b/lib/src/command/run.dart @@ -101,7 +101,8 @@ class RunCommand extends PubCommand { 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 5ade7ba02..c15a51eab 100644 --- a/lib/src/command/token_add.dart +++ b/lib/src/command/token_add.dart @@ -37,7 +37,7 @@ For interactive authorization against pub.dev, use `$topLevelProgram pub login`. @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 178d0a49b..61b797fd9 100644 --- a/lib/src/command/token_list.dart +++ b/lib/src/command/token_list.dart @@ -22,7 +22,7 @@ class TokenListCommand extends PubCommand { '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 b4a9a7735..f3f20c00e 100644 --- a/lib/src/command/token_remove.dart +++ b/lib/src/command/token_remove.dart @@ -6,6 +6,7 @@ import '../command.dart'; 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 @@ Remove secret token for package repository at .'''; @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 6c1dcda4e..bbe28ef95 100644 --- a/lib/src/command/upgrade.dart +++ b/lib/src/command/upgrade.dart @@ -35,7 +35,7 @@ class UpgradeCommand extends PubCommand { 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 @@ class UpgradeCommand extends PubCommand { /// 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 runProtected() async { @@ -114,7 +114,7 @@ Consider using the Dart 2.19 sdk to migrate to null safety.'''); } 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 @@ Consider using the Dart 2.19 sdk to migrate to null safety.'''); } 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 28ff85da8..e661d7247 100644 --- a/lib/src/command/uploader.dart +++ b/lib/src/command/uploader.dart @@ -23,7 +23,7 @@ class UploaderCommand extends PubCommand { 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 5fa9344ec..f9687fe92 100644 --- a/lib/src/command_runner.dart +++ b/lib/src/command_runner.dart @@ -32,6 +32,7 @@ import 'io.dart'; 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 @@ bool _isrunningInsideFlutter = class PubCommandRunner extends CommandRunner 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 @@ class PubCommandRunner extends CommandRunner implements PubTopLevel { 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 @@ class PubCommandRunner extends CommandRunner implements PubTopLevel { Future 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 ab8fed662..cb1eceed5 100644 --- a/lib/src/entrypoint.dart +++ b/lib/src/entrypoint.dart @@ -148,7 +148,7 @@ class Entrypoint { } 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 560d806b4..8c0955264 100644 --- a/lib/src/error_group.dart +++ b/lib/src/error_group.dart @@ -106,7 +106,7 @@ class ErrorGroup { /// /// 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 @@ class ErrorGroup { /// /// 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 @@ class ErrorGroup { _done._signalError(error, stackTrace); if (!caught && !_done._hasListeners) { scheduleMicrotask(() { + // ignore: only_throw_errors throw error; }); } @@ -234,7 +235,7 @@ class _ErrorGroupFuture implements Future { /// 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 @@ class _ErrorGroupStream extends Stream { /// 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 5ed343827..b9b25b627 100644 --- a/lib/src/global_packages.dart +++ b/lib/src/global_packages.dart @@ -615,7 +615,7 @@ try: 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 ab302fdf7..732381d67 100644 --- a/lib/src/http.dart +++ b/lib/src/http.dart @@ -178,13 +178,14 @@ extension AttachHeaders on http.Request { /// "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 @@ void handleJsonError(http.BaseResponse response) { 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 @@ void handleJsonError(http.BaseResponse response) { /// 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 086f93fca..386d176b3 100644 --- a/lib/src/io.dart +++ b/lib/src/io.dart @@ -742,7 +742,7 @@ Pair, Future> _consumerToSink(StreamConsumer consumer) { Future runProcess( String executable, List args, { - workingDir, + String? workingDir, Map? environment, bool runInShell = false, }) { @@ -765,8 +765,11 @@ Future runProcess( ); } - 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 @@ PubProcessResult runProcessSync( } 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 48de2fd71..b7fe219ae 100644 --- a/lib/src/lock_file.dart +++ b/lib/src/lock_file.dart @@ -55,10 +55,10 @@ class LockFile { Set? devDependencies, Set? 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 @@ class LockFile { return _wrapFormatException( 'Expected a $typeDescription', node.span, - () => parse(node.value), + () => parse(value), ); } else if (value is T) { return value; @@ -307,7 +307,7 @@ class LockFile { ) { 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 5a1299de7..9bc467f56 100644 --- a/lib/src/log.dart +++ b/lib/src/log.dart @@ -204,33 +204,32 @@ class _Entry { /// /// 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 @@ void processResult(String executable, PubProcessResult result) { } /// 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 @@ void exception(exception, [StackTrace? trace]) { } 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 @@ String gray(text) { /// 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 @@ class _JsonLogger { /// 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 d4053f8a6..d4d88c9f6 100644 --- a/lib/src/oauth2.dart +++ b/lib/src/oauth2.dart @@ -135,7 +135,8 @@ Future withClient(Future Function(Client) fn) { _clearCredentials(); return withClient(fn); } else { - throw error; + // ignore: only_throw_errors + throw error as Object; } }); } @@ -226,7 +227,7 @@ Future _authorize() async { // 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(); 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 b21555551..2123a4d8f 100644 --- a/lib/src/package_config.dart +++ b/lib/src/package_config.dart @@ -56,13 +56,13 @@ class PackageConfig { /// /// 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) { 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 @@ class PackageConfig { } final packages = []; 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 @@ class PackageConfig { // 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 @@ class PackageConfig { } 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 48c4258e5..06bba9e70 100644 --- a/lib/src/pub_embeddable_command.dart +++ b/lib/src/pub_embeddable_command.dart @@ -24,6 +24,7 @@ import 'command/upgrade.dart'; 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 @@ class PubEmbeddableCommand extends PubCommand implements PubTopLevel { 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 @@ class PubEmbeddableCommand extends PubCommand implements PubTopLevel { 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 c27fdb703..1b1e3b81f 100644 --- a/lib/src/pubspec.dart +++ b/lib/src/pubspec.dart @@ -102,10 +102,11 @@ class Pubspec extends PubspecBase { 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 @@ class Pubspec extends PubspecBase { 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 @@ class Pubspec extends PubspecBase { 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 @@ Map _parseDependencies( _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 @@ VersionConstraint _parseVersionConstraint( 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 @@ VersionConstraint _parseVersionConstraint( '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 df9fd3a7e..d4d6acb90 100644 --- a/lib/src/pubspec_parse.dart +++ b/lib/src/pubspec_parse.dart @@ -131,7 +131,7 @@ abstract class PubspecBase { } _parsedPublishTo = true; - _publishTo = publishTo; + _publishTo = publishTo as String?; return _publishTo; } @@ -161,7 +161,7 @@ abstract class PubspecBase { if (value is! String) { falseSecretsError(node.span); } - falseSecrets.add(value); + falseSecrets.add(value as String); } } else { falseSecretsError(falseSecretsNode.span); @@ -198,13 +198,17 @@ abstract class PubspecBase { ); } - 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 @@ abstract class PubspecBase { ); } - 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 @@ abstract class PubspecBase { } /// 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 ce794b794..45ec5b8a6 100644 --- a/lib/src/solver/package_lister.dart +++ b/lib/src/solver/package_lister.dart @@ -275,7 +275,8 @@ class PackageLister { 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 36401d68a..32ef867e4 100644 --- a/lib/src/source/git.dart +++ b/lib/src/source/git.dart @@ -95,7 +95,7 @@ class GitSource extends CachedSource { } 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 @@ class GitSource extends CachedSource { } 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 @@ class GitSource extends CachedSource { } 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 1b2840113..2d7376aa2 100644 --- a/lib/src/source/hosted.dart +++ b/lib/src/source/hosted.dart @@ -279,7 +279,7 @@ class HostedSource extends CachedSource { version, ResolvedHostedDescription( HostedDescription(name, url), - sha256: _parseContentHash(sha256), + sha256: _parseContentHash(sha256 as String?), ), ); } @@ -362,7 +362,7 @@ class HostedSource extends CachedSource { } final url = u ?? defaultUrl; - return HostedDescription(name, url); + return HostedDescription(name, url as String); } static final RegExp _looksLikePackageName = @@ -407,17 +407,29 @@ class HostedSource extends CachedSource { 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 @@ class HostedSource extends CachedSource { throw FormatException('version listing must be a mapping'); } body = decoded; - result = _versionInfoFromPackageListing(body, ref, url, cache); + result = _versionInfoFromPackageListing( + body as Map, + ref, + url, + cache, + ); } on Exception catch (error, stackTrace) { _throwFriendlyError(error, stackTrace, packageName, hostedUrl); } @@ -982,7 +999,7 @@ class HostedSource extends CachedSource { '${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 83358ea53..8db723e8e 100644 --- a/lib/src/source/path.dart +++ b/lib/src/source/path.dart @@ -117,7 +117,7 @@ class PathSource extends Source { } 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 971adbae9..ff2b53537 100644 --- a/lib/src/system_cache.dart +++ b/lib/src/system_cache.dart @@ -67,8 +67,10 @@ Consider setting the `PUB_CACHE` variable manually. })(); /// The available sources. - late final _sources = - Map.fromIterable([hosted, git, path, sdk], key: (source) => source.name); + late final _sources = Map.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 061bd7c30..4f144e4f1 100644 --- a/lib/src/utils.dart +++ b/lib/src/utils.dart @@ -9,6 +9,7 @@ import 'dart:io'; 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 @@ Future captureErrors( }) { var completer = Completer(); 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 @@ Future captureErrors( } else { stackTrace = Chain([]); } - if (!completer.isCompleted) completer.completeError(e, stackTrace); + if (!completer.isCompleted) { + completer.completeError(e, stackTrace); + } }); } @@ -147,12 +152,13 @@ Future captureErrors( Future> waitAndPrintErrors(Iterable> 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 @@ Future minByAsync( ) 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 @@ Future retry( 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 c51c6780d..0c2ac9a2c 100644 --- a/lib/src/validator/pubspec_typo.dart +++ b/lib/src/validator/pubspec_typo.dart @@ -19,6 +19,7 @@ class PubspecTypoValidator extends Validator { 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 728f5922d..50fd20814 100644 --- a/lib/src/validator/strict_dependencies.dart +++ b/lib/src/validator/strict_dependencies.dart @@ -35,7 +35,7 @@ class StrictDependenciesValidator extends Validator { } 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 c5fd2fb79..72f933905 100644 --- a/test/cache/list_test.dart +++ b/test/cache/list_test.dart @@ -9,7 +9,7 @@ import '../descriptor.dart' as d; 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 412be304a..4dbef3622 100644 --- a/test/dependency_services/dependency_services_test.dart +++ b/test/dependency_services/dependency_services_test.dart @@ -10,6 +10,7 @@ import 'package:pub/src/io.dart'; 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 @@ Future _listReportApply( 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 @@ Future main() async { '--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 @@ Future main() async { 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 @@ Future main() async { 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 d41001e03..38f9161c2 100644 --- a/test/descriptor.dart +++ b/test/descriptor.dart @@ -175,14 +175,18 @@ Descriptor gitPackageRepoCacheDir(String name) => /// 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 packages, { + int? port, + bool includePubspecs = false, +}) { var contents = []; 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 b508dc68a..2b644c28c 100644 --- a/test/descriptor/package_config.dart +++ b/test/descriptor/package_config.dart @@ -52,9 +52,7 @@ class PackageConfigFileDescriptor extends Descriptor { fail("File not found: '$packageConfigFile'."); } - Map 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 cf504d6da..6ab32adaf 100644 --- a/test/embedding/embedding_test.dart +++ b/test/embedding/embedding_test.dart @@ -92,7 +92,7 @@ Future main() async { 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 878189735..f56c57c65 100644 --- a/test/embedding/get_executable_for_command_test.dart +++ b/test/embedding/get_executable_for_command_test.dart @@ -17,7 +17,7 @@ import '../test_pub.dart'; Future 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 50a45f9b1..dd5473766 100644 --- a/test/lish/many_files_test.dart +++ b/test/lish/many_files_test.dart @@ -79,7 +79,7 @@ void main() { '${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 a2dd4f5db..b5d07bada 100644 --- a/test/package_server.dart +++ b/test/package_server.dart @@ -342,7 +342,7 @@ class _ServedPackageVersion { // 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 6c947516c..3a387c375 100644 --- a/test/rate_limited_scheduler_test.dart +++ b/test/rate_limited_scheduler_test.dart @@ -181,7 +181,7 @@ void main() { Future 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 8f84125b4..f79ac4bd7 100644 --- a/test/test_pub.dart +++ b/test/test_pub.dart @@ -149,7 +149,7 @@ Future pubCommand( int? exitCode, Map? 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 @@ Future runPub({ String? workingDirectory, Map? environment, List? input, - includeParentHomeAndPath = true, + bool includeParentHomeAndPath = true, }) async { exitCode ??= exit_codes.SUCCESS; // Cannot pass both output and outputJson. @@ -486,7 +486,7 @@ Future startPub({ String? workingDirectory, Map? environment, bool verbose = true, - includeParentHomeAndPath = true, + bool includeParentHomeAndPath = true, }) async { args ??= []; @@ -596,8 +596,8 @@ class PubProcess extends TestProcess { /// This is protected. PubProcess( - process, - description, { + Process process, + String description, { Encoding encoding = utf8, bool forwardStdio = false, }) : super( @@ -710,7 +710,7 @@ LockFile _createLockFile( Iterable? sandbox, Map? hosted, }) { - var dependencies = {}; + var dependencies = {}; if (sandbox != null) { for (var package in sandbox) { @@ -866,15 +866,15 @@ void _validateOutputString( void _validateOutputJson( List 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 @@ void _validateOutputJson( 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 9d71e3176..62d5576ea 100644 --- a/test/utils_test.dart +++ b/test/utils_test.dart @@ -142,8 +142,9 @@ b: {}'''), group('minByAsync', () { test('is stable', () async { { - final completers = {}; - Completer completer(k) => completers.putIfAbsent(k, Completer.new); + final completers = >{}; + Completer completer(String k) => + completers.putIfAbsent(k, Completer.new); Future lengthWhenComplete(String s) async { await completer(s).future; return s.length; @@ -153,15 +154,16 @@ b: {}'''), 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 = {}; - Completer completer(k) => completers.putIfAbsent(k, Completer.new); + final completers = >{}; + Completer completer(String k) => + completers.putIfAbsent(k, Completer.new); Future lengthWhenComplete(String s) async { await completer(s).future; return s.length; @@ -171,10 +173,10 @@ b: {}'''), 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 df3ae2b36..c5b89a5a6 100644 --- a/test/version_solver_test.dart +++ b/test/version_solver_test.dart @@ -1985,10 +1985,10 @@ Future expectResolves({ 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 5d69ab3d6..930172348 100644 --- a/tool/extract_all_pub_dev.dart +++ b/tool/extract_all_pub_dev.dart @@ -24,7 +24,7 @@ Future> allPackageNames() async { request.attachMetadataHeaders(); final response = await globalHttpClient.fetch(request); final result = json.decode(response.body); - return List.from(result['packages']); + return List.from(result['packages'] as List); } Future> versionArchiveUrls(String packageName) async { @@ -33,7 +33,9 @@ Future> versionArchiveUrls(String packageName) async { request.attachMetadataHeaders(); final response = await globalHttpClient.fetch(request); final result = json.decode(response.body); - return List.from(result['versions'].map((v) => v['archive_url'])); + return (result['versions'] as List) + .map((v) => v['archive_url'] as String) + .toList(); } Future main() async { @@ -41,11 +43,11 @@ Future main() async { var failures = ?>[]; 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); } } 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 9192079f6..36094d8bd 100644 --- a/tool/test-bin/pub_command_runner.dart +++ b/tool/test-bin/pub_command_runner.dart @@ -12,6 +12,7 @@ import 'package:pub/pub.dart'; 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 RunCommand extends Command { } class Runner extends CommandRunner { - 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 @@ class Runner extends CommandRunner { ) : null; addCommand( - pubCommand(analytics: analytics, isVerbose: () => _options['verbose']) + pubCommand( + analytics: analytics, + isVerbose: () => _results.flag('verbose'), + ) ..addSubcommand(ThrowingCommand()) ..addSubcommand(EnsurePubspecResolvedCommand()), ); @@ -98,11 +102,11 @@ class Runner extends CommandRunner { @override Future run(Iterable 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;