Enable 'strict-inference' language analysis mode (#4018)

diff --git a/analysis_options.yaml b/analysis_options.yaml
index f0bd15a..156085c 100644
--- a/analysis_options.yaml
+++ b/analysis_options.yaml
@@ -11,6 +11,7 @@
 
   language:
     strict-casts: true
+    strict-inference: true
 
 linter:
   rules:
diff --git a/lib/src/command.dart b/lib/src/command.dart
index 49c69ad..b2c5fe3 100644
--- a/lib/src/command.dart
+++ b/lib/src/command.dart
@@ -147,7 +147,7 @@
   @override
   String get invocation {
     PubCommand? command = this;
-    var names = [];
+    var names = <String?>[];
     do {
       names.add(command?.name);
       command = command?.parent as PubCommand?;
diff --git a/lib/src/command/dependency_services.dart b/lib/src/command/dependency_services.dart
index 8a0813a..c16e850 100644
--- a/lib/src/command/dependency_services.dart
+++ b/lib/src/command/dependency_services.dart
@@ -166,7 +166,7 @@
           _UpgradeType.compatible,
         ),
         'singleBreaking': kind != 'transitive' && singleBreakingVersion == null
-            ? []
+            ? <Object>[]
             : await computeUpgradeSet(
                 singleBreakingVersion,
                 _UpgradeType.singleBreaking,
@@ -176,7 +176,7 @@
                 multiBreakingVersion,
                 _UpgradeType.multiBreaking,
               )
-            : [],
+            : <Object>[],
         if (smallestUpgrade != null)
           'smallestUpdate': await computeUpgradeSet(
             smallestUpgrade,
@@ -503,7 +503,7 @@
       },
     );
     // Dummy message.
-    log.message(json.encode({'dependencies': []}));
+    log.message(json.encode({'dependencies': <Object>[]}));
   }
 }
 
diff --git a/lib/src/error_group.dart b/lib/src/error_group.dart
index 8c09552..27a065f 100644
--- a/lib/src/error_group.dart
+++ b/lib/src/error_group.dart
@@ -313,7 +313,7 @@
     if (_isDone) return;
     _subscription.cancel();
     // Call these asynchronously to work around issue 7913.
-    Future.value().then((_) {
+    Future<void>.value().then((_) {
       _controller.addError(e, stackTrace);
       _controller.close();
     });
diff --git a/lib/src/lock_file.dart b/lib/src/lock_file.dart
index cecf407..4df8f5b 100644
--- a/lib/src/lock_file.dart
+++ b/lib/src/lock_file.dart
@@ -420,7 +420,7 @@
   /// serialized as absolute.
   String serialize(String? packageDir, SystemCache cache) {
     // Convert the dependencies to a simple object.
-    var packageMap = {};
+    var packageMap = <String, Object?>{};
     for (final id in packages.values) {
       packageMap[id.name] = {
         'version': id.version.toString(),
@@ -432,7 +432,7 @@
     }
 
     var data = {
-      'sdks': mapMap(
+      'sdks': mapMap<String, SdkConstraint, String, String>(
         sdkConstraints,
         value: (_, constraint) => constraint.effectiveConstraint.toString(),
       ),
diff --git a/lib/src/oauth2.dart b/lib/src/oauth2.dart
index 8131ceb..9c6e1ec 100644
--- a/lib/src/oauth2.dart
+++ b/lib/src/oauth2.dart
@@ -124,7 +124,7 @@
       // Be sure to save the credentials even when an error happens.
       _saveCredentials(client.credentials);
     });
-  }).catchError((error) {
+  }).catchError((Object error) {
     if (error is _ExpirationException) {
       log.error("Pub's authorization to upload packages has expired and "
           "can't be automatically refreshed.");
@@ -139,7 +139,7 @@
       return withClient(fn);
     } else {
       // ignore: only_throw_errors
-      throw error as Object;
+      throw error;
     }
   });
 }
@@ -970,7 +970,7 @@
   ///
   /// Throws a [FormatException] if the JSON is incorrectly formatted.
   factory Credentials.fromJson(String json) {
-    void validate(bool condition, message) {
+    void validate(bool condition, String message) {
       if (condition) return;
       throw FormatException('Failed to load credentials: $message.\n\n$json');
     }
diff --git a/lib/src/source/hosted.dart b/lib/src/source/hosted.dart
index e99ee52..b3062e2 100644
--- a/lib/src/source/hosted.dart
+++ b/lib/src/source/hosted.dart
@@ -308,7 +308,7 @@
   /// this throws a descriptive FormatException.
   HostedDescription _parseDescription(
     String packageName,
-    description,
+    Object? description,
     LanguageVersion languageVersion,
   ) {
     if (description == null) {
diff --git a/lib/src/utils.dart b/lib/src/utils.dart
index d43780a..ccfb055 100644
--- a/lib/src/utils.dart
+++ b/lib/src/utils.dart
@@ -511,7 +511,7 @@
 String yamlToString(Object? data) {
   var buffer = StringBuffer();
 
-  void stringify(bool isMapValue, String indent, data) {
+  void stringify(bool isMapValue, String indent, Object? data) {
     // TODO(nweiz): Serialize using the YAML library once it supports
     // serialization.
 
@@ -757,7 +757,7 @@
     final rf = randomizationFactor * (random.nextDouble() * 2 - 1) + 1;
     final exp = math.min(attempt, 31); // prevent overflows.
     final delay = delayFactor * math.pow(2.0, exp) * rf;
-    await Future.delayed(delay < maxDelay ? delay : maxDelay);
+    await Future<void>.delayed(delay < maxDelay ? delay : maxDelay);
   }
 }
 
diff --git a/test/cache/list_test.dart b/test/cache/list_test.dart
index 7b4e5f5..eea83bb 100644
--- a/test/cache/list_test.dart
+++ b/test/cache/list_test.dart
@@ -23,7 +23,15 @@
       d.dir('hosted', [d.dir('pub.dev', [])]),
     ]).create();
 
-    await runPub(args: ['cache', 'list'], outputJson: {'packages': {}});
+    await runPub(
+      args: [
+        'cache',
+        'list',
+      ],
+      outputJson: {
+        'packages': <String, Object>{},
+      },
+    );
   });
 
   test('running pub cache list', () async {
diff --git a/test/dependency_services/dependency_services_test.dart b/test/dependency_services/dependency_services_test.dart
index 1e307fe..33937b3 100644
--- a/test/dependency_services/dependency_services_test.dart
+++ b/test/dependency_services/dependency_services_test.dart
@@ -631,9 +631,11 @@
 }
 
 dynamic findChangeVersion(dynamic json, String updateType, String name) {
-  final dep = json['dependencies'].firstWhere((p) => p['name'] == 'foo');
+  final dep =
+      json['dependencies'].firstWhere((dynamic p) => p['name'] == 'foo');
   if (dep == null) return null;
-  return dep[updateType].firstWhere((p) => p['name'] == name)['version'];
+  return dep[updateType]
+      .firstWhere((dynamic p) => p['name'] == name)['version'];
 }
 
 class _PackageVersion {
diff --git a/test/embedding/ensure_pubspec_resolved.dart b/test/embedding/ensure_pubspec_resolved.dart
index ba46efd..19654be 100644
--- a/test/embedding/ensure_pubspec_resolved.dart
+++ b/test/embedding/ensure_pubspec_resolved.dart
@@ -559,7 +559,7 @@
 Future _touch(String path) async {
   // Delay a bit to make sure the modification times are noticeably different.
   // 1s seems to be the finest granularity that dart:io reports.
-  await Future.delayed(Duration(seconds: 1));
+  await Future<void>.delayed(Duration(seconds: 1));
 
   path = p.join(d.sandbox, 'myapp', path);
   touch(path);
diff --git a/test/error_group_test.dart b/test/error_group_test.dart
index 15ffcf4..ec673bb 100644
--- a/test/error_group_test.dart
+++ b/test/error_group_test.dart
@@ -33,9 +33,13 @@
       expect(errorGroup.done, throwsFormatException);
       errorGroup.signalError(FormatException());
 
-      expect(() => errorGroup.registerFuture(Future.value()), throwsStateError);
       expect(
-        () => errorGroup.registerStream(StreamController(sync: true).stream),
+        () => errorGroup.registerFuture(Future<void>.value()),
+        throwsStateError,
+      );
+      expect(
+        () => errorGroup
+            .registerStream(StreamController<void>(sync: true).stream),
         throwsStateError,
       );
     });
@@ -63,12 +67,14 @@
       completer.complete('value');
 
       expect(
-        completer.future.then((_) => errorGroup.registerFuture(Future.value())),
+        completer.future
+            .then((_) => errorGroup.registerFuture(Future<void>.value())),
         throwsStateError,
       );
       expect(
         completer.future.then(
-          (_) => errorGroup.registerStream(StreamController(sync: true).stream),
+          (_) => errorGroup
+              .registerStream(StreamController<void>(sync: true).stream),
         ),
         throwsStateError,
       );
@@ -401,7 +407,7 @@
     test(
         "shouldn't throw a top-level exception if a stream receives an error "
         'after the other listened stream completes', () {
-      var signal = Completer();
+      var signal = Completer<void>();
       expect(
         stream1.toList().whenComplete(signal.complete),
         completion(equals(['value1', 'value2'])),
@@ -423,7 +429,7 @@
     test(
         "shouldn't throw a top-level exception if an error is signaled after "
         'one listened stream completes', () {
-      var signal = Completer();
+      var signal = Completer<void>();
       expect(
         stream1.toList().whenComplete(signal.complete),
         completion(equals(['value1', 'value2'])),
@@ -505,7 +511,7 @@
     test(
         "shouldn't throw a top-level exception if the future receives an "
         'error after the listened stream completes', () {
-      var signal = Completer();
+      var signal = Completer<void>();
       expect(
         stream.toList().whenComplete(signal.complete),
         completion(equals(['value1', 'value2'])),
diff --git a/test/lish/cloud_storage_upload_doesnt_redirect_test.dart b/test/lish/cloud_storage_upload_doesnt_redirect_test.dart
index f95db5e..38f32cf 100644
--- a/test/lish/cloud_storage_upload_doesnt_redirect_test.dart
+++ b/test/lish/cloud_storage_upload_doesnt_redirect_test.dart
@@ -20,7 +20,7 @@
     handleUploadForm(globalServer);
 
     globalServer.expect('POST', '/upload', (request) async {
-      await request.read().drain();
+      await request.read().drain<void>();
       return shelf.Response(200);
     });
 
diff --git a/test/lish/cloud_storage_upload_provides_an_error_test.dart b/test/lish/cloud_storage_upload_provides_an_error_test.dart
index a11ba95..431e433 100644
--- a/test/lish/cloud_storage_upload_provides_an_error_test.dart
+++ b/test/lish/cloud_storage_upload_provides_an_error_test.dart
@@ -20,7 +20,7 @@
     handleUploadForm(globalServer);
 
     globalServer.expect('POST', '/upload', (request) {
-      return request.read().drain().then((_) {
+      return request.read().drain<void>().then((_) {
         return shelf.Response.notFound(
           // Actual example of an error code we get from GCS
           "<?xml version='1.0' encoding='UTF-8'?><Error><Code>EntityTooLarge</Code><Message>Your proposed upload is larger than the maximum object size specified in your Policy Document.</Message><Details>Content-length exceeds upper bound on range</Details></Error>",
diff --git a/test/lish/utils.dart b/test/lish/utils.dart
index eebdc37..27df55f 100644
--- a/test/lish/utils.dart
+++ b/test/lish/utils.dart
@@ -34,7 +34,7 @@
     // that the request body is correctly formatted. See issue 6952.
     return request
         .read()
-        .drain()
+        .drain<void>()
         .then((_) => server.url)
         .then((url) => shelf.Response.found(Uri.parse(url).resolve('/create')));
   });
diff --git a/test/oauth2/with_a_server_rejected_refresh_token_authenticates_again_test.dart b/test/oauth2/with_a_server_rejected_refresh_token_authenticates_again_test.dart
index 419a81e..0669cb6 100644
--- a/test/oauth2/with_a_server_rejected_refresh_token_authenticates_again_test.dart
+++ b/test/oauth2/with_a_server_rejected_refresh_token_authenticates_again_test.dart
@@ -32,7 +32,7 @@
     var pub = await startPublish(globalServer);
 
     globalServer.expect('POST', '/token', (request) {
-      return request.read().drain().then((_) {
+      return request.read().drain<void>().then((_) {
         return shelf.Response(
           400,
           body: jsonEncode({'error': 'invalid_request'}),
@@ -46,7 +46,7 @@
     await expectLater(pub.stdout, emits(startsWith('Uploading...')));
     await authorizePub(pub, globalServer, 'new access token');
 
-    var done = Completer();
+    var done = Completer<void>();
     globalServer.expect('GET', '/api/packages/versions/new', (request) async {
       expect(
         request.headers,
diff --git a/test/test_pub.dart b/test/test_pub.dart
index 951ee21..cad7f30 100644
--- a/test/test_pub.dart
+++ b/test/test_pub.dart
@@ -79,7 +79,7 @@
 Map<String, dynamic> packageSpec(String packageName) => json
     .decode(File(d.path(packageConfigFilePath)).readAsStringSync())['packages']
     .firstWhere(
-      (e) => e['name'] == packageName,
+      (dynamic e) => e['name'] == packageName,
       orElse: () => null,
     ) as Map<String, dynamic>;
 
@@ -796,7 +796,7 @@
 void _validateOutput(
   List<String> failures,
   String pipe,
-  expected,
+  Object? expected,
   String actual,
 ) {
   if (expected == null) return;
@@ -884,7 +884,7 @@
   // Remove dart2js's timing logs, which would otherwise cause tests to fail
   // flakily when compilation takes a long time.
   actual['log']?.removeWhere(
-    (entry) =>
+    (dynamic entry) =>
         entry['level'] == 'Fine' &&
         (entry['message'] as String).startsWith('Not yet complete after'),
   );
diff --git a/test/token/remove_token_test.dart b/test/token/remove_token_test.dart
index dd4f4c4..e06459e 100644
--- a/test/token/remove_token_test.dart
+++ b/test/token/remove_token_test.dart
@@ -19,7 +19,9 @@
 
     await runPub(args: ['token', 'remove', 'https://server.demo']);
 
-    await d.tokensFile({'version': 1, 'hosted': []}).validate();
+    await d.tokensFile(
+      {'version': 1, 'hosted': <Map<String, String>>[]},
+    ).validate();
   });
 
   test('without any matching schemes, does nothing', () async {
diff --git a/test/token/when_receives_401_removes_token_test.dart b/test/token/when_receives_401_removes_token_test.dart
index 13816cc..ad90b33 100644
--- a/test/token/when_receives_401_removes_token_test.dart
+++ b/test/token/when_receives_401_removes_token_test.dart
@@ -27,6 +27,8 @@
 
     await pub.shouldExit(65);
 
-    await d.tokensFile({'version': 1, 'hosted': []}).validate();
+    await d.tokensFile(
+      {'version': 1, 'hosted': <Map<String, Object?>>[]},
+    ).validate();
   });
 }
diff --git a/test/utils_test.dart b/test/utils_test.dart
index 173041d..1ce8869 100644
--- a/test/utils_test.dart
+++ b/test/utils_test.dart
@@ -75,7 +75,7 @@
     });
 
     test('handles non-string map keys', () {
-      var map = {};
+      var map = <Object?, Object?>{};
       map[null] = 'null';
       map[123] = 'num';
       map[true] = 'bool';
@@ -92,7 +92,7 @@
     test('handles empty maps', () {
       expect(yamlToString({}), equals('{}'));
       expect(
-        yamlToString({'a': {}, 'b': {}}),
+        yamlToString({'a': <Object, Object>{}, 'b': <Object, Object>{}}),
         equals('''
 a: {}
 b: {}'''),
@@ -188,7 +188,7 @@
     expect(() => hexEncode([256, 0, 1]), throwsA(isA<FormatException>()));
   });
   test('hexDecode', () {
-    expect(hexDecode(''), []);
+    expect(hexDecode(''), <int>[]);
     expect(hexDecode('ff0001f0abcdef'), [255, 0, 1, 240, 171, 205, 239]);
     expect(hexDecode('FF0001F0ABCDEF'), [255, 0, 1, 240, 171, 205, 239]);
     expect(() => hexDecode('F'), throwsA(isA<FormatException>()));
diff --git a/tool/extract_all_pub_dev.dart b/tool/extract_all_pub_dev.dart
index ffd08a2..9525a19 100644
--- a/tool/extract_all_pub_dev.dart
+++ b/tool/extract_all_pub_dev.dart
@@ -47,7 +47,8 @@
     for (final packageName in json['packages'] as Iterable? ?? []) {
       alreadyDonePackages.add(packageName as String);
     }
-    for (final failure in (json['failures'] ?? []) as Iterable) {
+    for (final failure
+        in (json['failures'] ?? <Map<String, dynamic>>[]) as Iterable) {
       failures.add(failure as Map<String, dynamic>);
     }
   }