migrate to null safety (dart-lang/coverage#332)

I did not do large refactors for the most part here I left in existing assumptions which means a lot of `!` are added.

Note that the `_shouldIgnoreLine` private method was moved to a local method and the patterns around it changed a fair bit due to iterators changing with null safety (it previously relied on `current` being null and now uses the result of `moveNext`).
diff --git a/pkgs/coverage/CHANGELOG.md b/pkgs/coverage/CHANGELOG.md
index 9a63f96..0a0d86e 100644
--- a/pkgs/coverage/CHANGELOG.md
+++ b/pkgs/coverage/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 1.0.0 - 2021-02-25
+
+* Migrate to null safety.
+
 ## 0.15.2 - 2021-02-08
 
 * Update `args`, `logging`, and `package_config` deps to allow the latest
diff --git a/pkgs/coverage/bin/collect_coverage.dart b/pkgs/coverage/bin/collect_coverage.dart
index e49e62f..ccead74 100644
--- a/pkgs/coverage/bin/collect_coverage.dart
+++ b/pkgs/coverage/bin/collect_coverage.dart
@@ -39,7 +39,7 @@
 
   final Uri serviceUri;
   final IOSink out;
-  final Duration timeout;
+  final Duration? timeout;
   final bool waitPaused;
   final bool resume;
   final bool includeDart;
@@ -81,7 +81,7 @@
     print(parser.usage);
   }
 
-  void fail(String message) {
+  Never fail(String message) {
     print('Error: $message\n');
     printUsage();
     exit(1);
@@ -105,8 +105,7 @@
     }
   }
 
-  final scopedOutput = args['scope-output'] as List<String> ?? [];
-
+  final scopedOutput = args['scope-output'] as List<String>;
   IOSink out;
   if (args['out'] == 'stdout') {
     out = stdout;
diff --git a/pkgs/coverage/bin/format_coverage.dart b/pkgs/coverage/bin/format_coverage.dart
index a0cdf50..ab643ed 100644
--- a/pkgs/coverage/bin/format_coverage.dart
+++ b/pkgs/coverage/bin/format_coverage.dart
@@ -10,20 +10,35 @@
 
 /// [Environment] stores gathered arguments information.
 class Environment {
-  String sdkRoot;
-  String packagesPath;
-  String baseDirectory;
-  String input;
-  IOSink output;
-  List<String> reportOn;
-  String bazelWorkspace;
+  Environment({
+    required this.baseDirectory,
+    required this.bazel,
+    required this.bazelWorkspace,
+    required this.checkIgnore,
+    required this.input,
+    required this.lcov,
+    required this.output,
+    required this.packagesPath,
+    required this.prettyPrint,
+    required this.reportOn,
+    required this.sdkRoot,
+    required this.verbose,
+    required this.workers,
+  });
+
+  String? baseDirectory;
   bool bazel;
-  int workers;
-  bool prettyPrint;
-  bool lcov;
-  bool expectMarkers;
+  String bazelWorkspace;
   bool checkIgnore;
+  String input;
+  bool lcov;
+  IOSink output;
+  String? packagesPath;
+  bool prettyPrint;
+  List<String>? reportOn;
+  String? sdkRoot;
   bool verbose;
+  int workers;
 }
 
 Future<Null> main(List<String> arguments) async {
@@ -55,9 +70,7 @@
   String output;
   final resolver = env.bazel
       ? BazelResolver(workspacePath: env.bazelWorkspace)
-      : Resolver(
-          packagesPath: env.packagesPath,
-          sdkRoot: env.sdkRoot);
+      : Resolver(packagesPath: env.packagesPath, sdkRoot: env.sdkRoot);
   final loader = Loader();
   if (env.prettyPrint) {
     output =
@@ -96,7 +109,6 @@
 /// Checks the validity of the provided arguments. Does not initialize actual
 /// processing.
 Environment parseArgs(List<String> arguments) {
-  final env = Environment();
   final parser = ArgParser();
 
   parser.addOption('sdk-root', abbr: 's', help: 'path to the SDK root');
@@ -141,7 +153,7 @@
     print(parser.usage);
   }
 
-  void fail(String msg) {
+  Never fail(String msg) {
     print('\n$msg\n');
     printUsage();
     exit(1);
@@ -152,66 +164,83 @@
     exit(0);
   }
 
-  env.sdkRoot = args['sdk-root'] as String;
-  if (env.sdkRoot != null) {
-    env.sdkRoot = p.normalize(p.join(p.absolute(env.sdkRoot), 'lib'));
-    if (!FileSystemEntity.isDirectorySync(env.sdkRoot)) {
+  var sdkRoot = args['sdk-root'] as String?;
+  if (sdkRoot != null) {
+    sdkRoot = p.normalize(p.join(p.absolute(sdkRoot), 'lib'));
+    if (!FileSystemEntity.isDirectorySync(sdkRoot)) {
       fail('Provided SDK root "${args["sdk-root"]}" is not a valid SDK '
           'top-level directory');
     }
   }
 
-  env.packagesPath = args['packages'] as String;
-  if (env.packagesPath != null) {
-    if (!FileSystemEntity.isFileSync(env.packagesPath)) {
+  final packagesPath = args['packages'] as String?;
+  if (packagesPath != null) {
+    if (!FileSystemEntity.isFileSync(packagesPath)) {
       fail('Package spec "${args["packages"]}" not found, or not a file.');
     }
   }
 
   if (args['in'] == null) fail('No input files given.');
-  env.input = p.absolute(p.normalize(args['in'] as String));
-  if (!FileSystemEntity.isDirectorySync(env.input) &&
-      !FileSystemEntity.isFileSync(env.input)) {
+  final input = p.absolute(p.normalize(args['in'] as String));
+  if (!FileSystemEntity.isDirectorySync(input) &&
+      !FileSystemEntity.isFileSync(input)) {
     fail('Provided input "${args["in"]}" is neither a directory nor a file.');
   }
 
+  IOSink output;
   if (args['out'] == 'stdout') {
-    env.output = stdout;
+    output = stdout;
   } else {
     final outpath = p.absolute(p.normalize(args['out'] as String));
     final outfile = File(outpath)..createSync(recursive: true);
-    env.output = outfile.openWrite();
+    output = outfile.openWrite();
   }
 
-  final reportOn = args['report-on'] as List<String>;
-  env.reportOn = reportOn.isNotEmpty ? reportOn : null;
+  final reportOnRaw = args['report-on'] as List<String>;
+  final reportOn = reportOnRaw.isNotEmpty ? reportOnRaw : null;
 
-  env.bazel = args['bazel'] as bool;
-  env.bazelWorkspace = args['bazel-workspace'] as String;
-  if (env.bazelWorkspace.isNotEmpty && !env.bazel) {
+  final bazel = args['bazel'] as bool;
+  final bazelWorkspace = args['bazel-workspace'] as String;
+  if (bazelWorkspace.isNotEmpty && !bazel) {
     stderr.writeln('warning: ignoring --bazel-workspace: --bazel not set');
   }
 
+  String? baseDirectory;
   if (args['base-directory'] != null) {
-    env.baseDirectory = p.absolute(args['base-directory'] as String);
+    baseDirectory = p.absolute(args['base-directory'] as String);
   }
 
-  env.lcov = args['lcov'] as bool;
-  if (args['pretty-print'] as bool && env.lcov) {
+  final lcov = args['lcov'] as bool;
+  if (args['pretty-print'] as bool && lcov == true) {
     fail('Choose one of pretty-print or lcov output');
   }
-  // Use pretty-print either explicitly or by default.
-  env.prettyPrint = !env.lcov;
 
+  // Use pretty-print either explicitly or by default.
+  final prettyPrint = !lcov;
+
+  int workers;
   try {
-    env.workers = int.parse('${args["workers"]}');
+    workers = int.parse('${args["workers"]}');
   } catch (e) {
     fail('Invalid worker count: $e');
   }
 
-  env.checkIgnore = args['check-ignore'] as bool;
-  env.verbose = args['verbose'] as bool;
-  return env;
+  final checkIgnore = args['check-ignore'] as bool;
+  final verbose = args['verbose'] as bool;
+  return Environment(
+      baseDirectory: baseDirectory,
+      bazel: bazel,
+      bazelWorkspace: bazelWorkspace,
+      checkIgnore: checkIgnore,
+      input: input,
+      lcov: lcov,
+      output: output,
+      packagesPath: packagesPath,
+      prettyPrint: prettyPrint,
+      reportOn: reportOn,
+      sdkRoot: sdkRoot,
+      verbose: verbose,
+      workers: workers);
 }
 
 /// Given an absolute path absPath, this function returns a [List] of files
diff --git a/pkgs/coverage/lib/src/chrome.dart b/pkgs/coverage/lib/src/chrome.dart
index 5246c07..33a0e1e 100644
--- a/pkgs/coverage/lib/src/chrome.dart
+++ b/pkgs/coverage/lib/src/chrome.dart
@@ -21,8 +21,8 @@
 /// content is null will be ignored.
 Future<Map<String, dynamic>> parseChromeCoverage(
   List<Map<String, dynamic>> preciseCoverage,
-  Future<String> Function(String scriptId) sourceProvider,
-  Future<String> Function(String scriptId) sourceMapProvider,
+  Future<String?> Function(String scriptId) sourceProvider,
+  Future<String?> Function(String scriptId) sourceMapProvider,
   Future<Uri> Function(String sourceUrl, String scriptId) sourceUriProvider,
 ) async {
   final coverageReport = <Uri, Map<int, bool>>{};
@@ -50,8 +50,9 @@
 
     for (var lineEntry in mapping.lines) {
       for (var columnEntry in lineEntry.entries) {
-        if (columnEntry.sourceUrlId == null) continue;
-        final sourceUrl = mapping.urls[columnEntry.sourceUrlId];
+        final sourceUrlId = columnEntry.sourceUrlId;
+        if (sourceUrlId == null) continue;
+        final sourceUrl = mapping.urls[sourceUrlId];
 
         // Ignore coverage information for the SDK.
         if (sourceUrl.startsWith('org-dartlang-sdk:')) continue;
@@ -59,8 +60,9 @@
         final uri = await sourceUriProvider(sourceUrl, scriptId);
         final coverage = coverageReport.putIfAbsent(uri, () => <int, bool>{});
 
-        final current = coverage[columnEntry.sourceLine + 1] ?? false;
-        coverage[columnEntry.sourceLine + 1] = current ||
+        final sourceLine = columnEntry.sourceLine!;
+        final current = coverage[sourceLine + 1] ?? false;
+        coverage[sourceLine + 1] = current ||
             coveredPositions.contains(
                 _Position(lineEntry.line + 1, columnEntry.column + 1));
       }
@@ -71,7 +73,7 @@
   coverageReport.forEach((uri, coverage) {
     final hitMap = <int, int>{};
     for (var line in coverage.keys.toList()..sort()) {
-      hitMap[line] = coverage[line] ? 1 : 0;
+      hitMap[line] = coverage[line]! ? 1 : 0;
     }
     coverageHitMaps[uri] = hitMap;
   });
diff --git a/pkgs/coverage/lib/src/collect.dart b/pkgs/coverage/lib/src/collect.dart
index 24cdd8e..29be493 100644
--- a/pkgs/coverage/lib/src/collect.dart
+++ b/pkgs/coverage/lib/src/collect.dart
@@ -35,17 +35,16 @@
 /// if [isolateIds] is set, the coverage gathering will be restricted to only
 /// those VM isolates.
 Future<Map<String, dynamic>> collect(Uri serviceUri, bool resume,
-    bool waitPaused, bool includeDart, Set<String> scopedOutput,
-    {Set<String> isolateIds, Duration timeout}) async {
+    bool waitPaused, bool includeDart, Set<String>? scopedOutput,
+    {Set<String>? isolateIds, Duration? timeout}) async {
   scopedOutput ??= <String>{};
-  if (serviceUri == null) throw ArgumentError('serviceUri must not be null');
 
   // Create websocket URI. Handle any trailing slashes.
   final pathSegments =
       serviceUri.pathSegments.where((c) => c.isNotEmpty).toList()..add('ws');
   final uri = serviceUri.replace(scheme: 'ws', pathSegments: pathSegments);
 
-  VmService service;
+  late VmService service;
   await retry(() async {
     try {
       final options = const CompressionOptions(enabled: false);
@@ -83,24 +82,27 @@
   }
 }
 
-Future<Map<String, dynamic>> _getAllCoverage(VmService service,
-    bool includeDart, Set<String> scopedOutput, Set<String> isolateIds) async {
+Future<Map<String, dynamic>> _getAllCoverage(
+    VmService service,
+    bool includeDart,
+    Set<String>? scopedOutput,
+    Set<String>? isolateIds) async {
   scopedOutput ??= <String>{};
   final vm = await service.getVM();
   final allCoverage = <Map<String, dynamic>>[];
 
-  for (var isolateRef in vm.isolates) {
+  for (var isolateRef in vm.isolates!) {
     if (isolateIds != null && !isolateIds.contains(isolateRef.id)) continue;
     if (scopedOutput.isNotEmpty) {
-      final scripts = await service.getScripts(isolateRef.id);
-      for (var script in scripts.scripts) {
-        final uri = Uri.parse(script.uri);
+      final scripts = await service.getScripts(isolateRef.id!);
+      for (var script in scripts.scripts!) {
+        final uri = Uri.parse(script.uri!);
         if (uri.scheme != 'package') continue;
         final scope = uri.path.split('/').first;
         // Skip scripts which should not be included in the report.
         if (!scopedOutput.contains(scope)) continue;
         final scriptReport = await service.getSourceReport(
-            isolateRef.id, <String>[SourceReportKind.kCoverage],
+            isolateRef.id!, <String>[SourceReportKind.kCoverage],
             forceCompile: true, scriptId: script.id);
         final coverage = await _getCoverageJson(
             service, isolateRef, scriptReport, includeDart);
@@ -108,7 +110,7 @@
       }
     } else {
       final isolateReport = await service.getSourceReport(
-        isolateRef.id,
+        isolateRef.id!,
         <String>[SourceReportKind.kCoverage],
         forceCompile: true,
       );
@@ -123,14 +125,14 @@
 Future _resumeIsolates(VmService service) async {
   final vm = await service.getVM();
   final futures = <Future>[];
-  for (var isolateRef in vm.isolates) {
+  for (var isolateRef in vm.isolates!) {
     // Guard against sync as well as async errors: sync - when we are writing
     // message to the socket, the socket might be closed; async - when we are
     // waiting for the response, the socket again closes.
     futures.add(Future.sync(() async {
-      final isolate = await service.getIsolate(isolateRef.id);
-      if (isolate.pauseEvent.kind != EventKind.kResume) {
-        await service.resume(isolateRef.id);
+      final isolate = await service.getIsolate(isolateRef.id!);
+      if (isolate.pauseEvent!.kind != EventKind.kResume) {
+        await service.resume(isolateRef.id!);
       }
     }));
   }
@@ -141,7 +143,7 @@
   }
 }
 
-Future _waitIsolatesPaused(VmService service, {Duration timeout}) async {
+Future _waitIsolatesPaused(VmService service, {Duration? timeout}) async {
   final pauseEvents = <String>{
     EventKind.kPauseStart,
     EventKind.kPauseException,
@@ -152,10 +154,10 @@
 
   Future allPaused() async {
     final vm = await service.getVM();
-    if (vm.isolates.isEmpty) throw 'No isolates.';
-    for (var isolateRef in vm.isolates) {
-      final isolate = await service.getIsolate(isolateRef.id);
-      if (!pauseEvents.contains(isolate.pauseEvent.kind)) {
+    if (vm.isolates!.isEmpty) throw 'No isolates.';
+    for (var isolateRef in vm.isolates!) {
+      final isolate = await service.getIsolate(isolateRef.id!);
+      if (!pauseEvents.contains(isolate.pauseEvent!.kind)) {
         throw 'Unpaused isolates remaining.';
       }
     }
@@ -168,14 +170,14 @@
 ///
 /// Performs a binary search within the script's token position table to locate
 /// the line in question.
-int _getLineFromTokenPos(Script script, int tokenPos) {
+int? _getLineFromTokenPos(Script script, int tokenPos) {
   // TODO(cbracken): investigate whether caching this lookup results in
   // significant performance gains.
   var min = 0;
-  var max = script.tokenPosTable.length;
+  var max = script.tokenPosTable!.length;
   while (min < max) {
     final mid = min + ((max - min) >> 1);
-    final row = script.tokenPosTable[mid];
+    final row = script.tokenPosTable![mid];
     if (row[1] > tokenPos) {
       max = mid;
     } else {
@@ -194,9 +196,9 @@
   // script uri -> { line -> hit count }
   final hitMaps = <Uri, Map<int, int>>{};
   final scripts = <ScriptRef, Script>{};
-  for (var range in report.ranges) {
-    final scriptRef = report.scripts[range.scriptIndex];
-    final scriptUri = Uri.parse(report.scripts[range.scriptIndex].uri);
+  for (var range in report.ranges!) {
+    final scriptRef = report.scripts![range.scriptIndex!];
+    final scriptUri = Uri.parse(report.scripts![range.scriptIndex!].uri!);
 
     // Not returned in scripts section of source report.
     if (scriptUri.scheme == 'evaluate') continue;
@@ -206,9 +208,10 @@
 
     if (!scripts.containsKey(scriptRef)) {
       scripts[scriptRef] =
-          await service.getObject(isolateRef.id, scriptRef.id) as Script;
+          await service.getObject(isolateRef.id!, scriptRef.id!) as Script;
     }
     final script = scripts[scriptRef];
+    if (script == null) continue;
 
     // Look up the hit map for this script (shared across isolates).
     final hitMap = hitMaps.putIfAbsent(scriptUri, () => <int, int>{});
@@ -218,17 +221,19 @@
 
     if (coverage == null) continue;
 
-    for (final tokenPos in coverage.hits) {
+    for (final tokenPos in coverage.hits!) {
       final line = _getLineFromTokenPos(script, tokenPos);
       if (line == null) {
         print('tokenPos $tokenPos has no line mapping for script $scriptUri');
+        continue;
       }
-      hitMap[line] = hitMap.containsKey(line) ? hitMap[line] + 1 : 1;
+      hitMap[line] = hitMap.containsKey(line) ? hitMap[line]! + 1 : 1;
     }
-    for (final tokenPos in coverage.misses) {
+    for (final tokenPos in coverage.misses!) {
       final line = _getLineFromTokenPos(script, tokenPos);
       if (line == null) {
         print('tokenPos $tokenPos has no line mapping for script $scriptUri');
+        continue;
       }
       hitMap.putIfAbsent(line, () => 0);
     }
diff --git a/pkgs/coverage/lib/src/formatter.dart b/pkgs/coverage/lib/src/formatter.dart
index e436033..679b8f3 100644
--- a/pkgs/coverage/lib/src/formatter.dart
+++ b/pkgs/coverage/lib/src/formatter.dart
@@ -25,15 +25,15 @@
   LcovFormatter(this.resolver, {this.reportOn, this.basePath});
 
   final Resolver resolver;
-  final String basePath;
-  final List<String> reportOn;
+  final String? basePath;
+  final List<String>? reportOn;
 
   @override
   Future<String> format(Map<String, Map<int, int>> hitmap) async {
     final pathFilter = _getPathFilter(reportOn);
     final buf = StringBuffer();
     for (var key in hitmap.keys) {
-      final v = hitmap[key];
+      final v = hitmap[key]!;
       var source = resolver.resolve(key);
       if (source == null) {
         continue;
@@ -53,7 +53,7 @@
         buf.write('DA:$k,${v[k]}\n');
       }
       buf.write('LF:${lines.length}\n');
-      buf.write('LH:${lines.where((k) => v[k] > 0).length}\n');
+      buf.write('LH:${lines.where((k) => v[k]! > 0).length}\n');
       buf.write('end_of_record\n');
     }
 
@@ -75,7 +75,7 @@
 
   final Resolver resolver;
   final Loader loader;
-  final List<String> reportOn;
+  final List<String>? reportOn;
 
   @override
   Future<String> format(Map<String, dynamic> hitmap) async {
@@ -114,7 +114,7 @@
 
 typedef _PathFilter = bool Function(String path);
 
-_PathFilter _getPathFilter(List<String> reportOn) {
+_PathFilter _getPathFilter(List<String>? reportOn) {
   if (reportOn == null) return (String path) => true;
 
   final absolutePaths = reportOn.map(p.absolute).toList();
diff --git a/pkgs/coverage/lib/src/hitmap.dart b/pkgs/coverage/lib/src/hitmap.dart
index 68397a5..fa32a27 100644
--- a/pkgs/coverage/lib/src/hitmap.dart
+++ b/pkgs/coverage/lib/src/hitmap.dart
@@ -15,7 +15,7 @@
 Future<Map<String, Map<int, int>>> createHitmap(
   List<Map<String, dynamic>> jsonResult, {
   bool checkIgnoredLines = false,
-  String packagesPath,
+  String? packagesPath,
 }) async {
   final resolver = Resolver(packagesPath: packagesPath);
   final loader = Loader();
@@ -29,7 +29,7 @@
   }
 
   for (var e in jsonResult) {
-    final source = e['source'] as String;
+    final source = e['source'] as String?;
     if (source == null) {
       // Couldn't resolve import, so skip this entry.
       continue;
@@ -38,20 +38,46 @@
     var ignoredLinesList = <List<int>>[];
 
     if (checkIgnoredLines) {
-      final lines = await loader.load(resolver.resolve(source));
-      ignoredLinesList = getIgnoredLines(lines);
+      final path = resolver.resolve(source);
+      if (path != null) {
+        final lines = await loader.load(path);
+        ignoredLinesList = getIgnoredLines(lines!);
 
-      // Ignore the whole file.
-      if (ignoredLinesList.length == 1 &&
-          ignoredLinesList[0][0] == 0 &&
-          ignoredLinesList[0][1] == lines.length) {
-        continue;
+        // Ignore the whole file.
+        if (ignoredLinesList.length == 1 &&
+            ignoredLinesList[0][0] == 0 &&
+            ignoredLinesList[0][1] == lines.length) {
+          continue;
+        }
       }
     }
 
     // Move to the first ignore range.
     final ignoredLines = ignoredLinesList.iterator;
-    ignoredLines.moveNext();
+    var hasCurrent = ignoredLines.moveNext();
+
+    bool _shouldIgnoreLine(Iterator<List<int>> ignoredRanges, int line) {
+      if (!hasCurrent || ignoredRanges.current.isEmpty) {
+        return false;
+      }
+
+      if (line < ignoredRanges.current[0]) return false;
+
+      while (hasCurrent &&
+          ignoredRanges.current.isNotEmpty &&
+          ignoredRanges.current[1] < line) {
+        hasCurrent = ignoredRanges.moveNext();
+      }
+
+      if (hasCurrent &&
+          ignoredRanges.current.isNotEmpty &&
+          ignoredRanges.current[0] <= line &&
+          line <= ignoredRanges.current[1]) {
+        return true;
+      }
+
+      return false;
+    }
 
     final sourceHitMap = globalHitMap.putIfAbsent(source, () => <int, int>{});
     final hits = e['hits'] as List;
@@ -84,39 +110,18 @@
   return globalHitMap;
 }
 
-bool _shouldIgnoreLine(Iterator<List<int>> ignoredRanges, int line) {
-  if (ignoredRanges.current == null || ignoredRanges.current.isEmpty) {
-    return false;
-  }
-
-  if (line < ignoredRanges.current[0]) return false;
-
-  while (ignoredRanges.current != null &&
-      ignoredRanges.current.isNotEmpty &&
-      ignoredRanges.current[1] < line) {
-    ignoredRanges.moveNext();
-  }
-
-  if (ignoredRanges.current != null &&
-      ignoredRanges.current.isNotEmpty &&
-      ignoredRanges.current[0] <= line &&
-      line <= ignoredRanges.current[1]) {
-    return true;
-  }
-
-  return false;
-}
-
 /// Merges [newMap] into [result].
 void mergeHitmaps(
     Map<String, Map<int, int>> newMap, Map<String, Map<int, int>> result) {
   newMap.forEach((String file, Map<int, int> v) {
-    if (result.containsKey(file)) {
+    final fileResult = result[file];
+    if (fileResult != null) {
       v.forEach((int line, int cnt) {
-        if (result[file][line] == null) {
-          result[file][line] = cnt;
+        final lineFileResult = fileResult[line];
+        if (lineFileResult == null) {
+          fileResult[line] = cnt;
         } else {
-          result[file][line] += cnt;
+          fileResult[line] = lineFileResult + cnt;
         }
       });
     } else {
diff --git a/pkgs/coverage/lib/src/resolver.dart b/pkgs/coverage/lib/src/resolver.dart
index 751e57f..6415e6c 100644
--- a/pkgs/coverage/lib/src/resolver.dart
+++ b/pkgs/coverage/lib/src/resolver.dart
@@ -10,20 +10,21 @@
 
 /// [Resolver] resolves imports with respect to a given environment.
 class Resolver {
-  Resolver({String packagesPath, this.sdkRoot})
+  Resolver({String? packagesPath, this.sdkRoot})
       : packagesPath = packagesPath,
         _packages = packagesPath != null ? _parsePackages(packagesPath) : null;
 
-  final String packagesPath;
-  final String sdkRoot;
+  final String? packagesPath;
+  final String? sdkRoot;
   final List<String> failed = [];
-  final Map<String, Uri> _packages;
+  final Map<String, Uri>? _packages;
 
   /// Returns the absolute path wrt. to the given environment or null, if the
   /// import could not be resolved.
-  String resolve(String scriptUri) {
+  String? resolve(String scriptUri) {
     final uri = Uri.parse(scriptUri);
     if (uri.scheme == 'dart') {
+      final sdkRoot = this.sdkRoot;
       if (sdkRoot == null) {
         // No sdk-root given, do not resolve dart: URIs.
         return null;
@@ -53,6 +54,7 @@
       return resolveSymbolicLinks(filePath);
     }
     if (uri.scheme == 'package') {
+      final _packages = this._packages;
       if (_packages == null) {
         return null;
       }
@@ -76,7 +78,7 @@
   }
 
   /// Returns a canonicalized path, or `null` if the path cannot be resolved.
-  String resolveSymbolicLinks(String path) {
+  String? resolveSymbolicLinks(String path) {
     final normalizedPath = p.normalize(path);
     final type = FileSystemEntity.typeSync(normalizedPath, followLinks: true);
     if (type == FileSystemEntityType.notFound) return null;
@@ -123,7 +125,7 @@
   /// Returns the absolute path wrt. to the given environment or null, if the
   /// import could not be resolved.
   @override
-  String resolve(String scriptUri) {
+  String? resolve(String scriptUri) {
     final uri = Uri.parse(scriptUri);
     if (uri.scheme == 'dart') {
       // Ignore the SDK
@@ -177,7 +179,7 @@
 
   /// Loads an imported resource and returns a [Future] with a [List] of lines.
   /// Returns `null` if the resource could not be loaded.
-  Future<List<String>> load(String path) async {
+  Future<List<String>?> load(String path) async {
     try {
       // Ensure `readAsLines` runs within the try block so errors are caught.
       return await File(path).readAsLines();
diff --git a/pkgs/coverage/lib/src/run_and_collect.dart b/pkgs/coverage/lib/src/run_and_collect.dart
index 78573e7..6e52f4f 100644
--- a/pkgs/coverage/lib/src/run_and_collect.dart
+++ b/pkgs/coverage/lib/src/run_and_collect.dart
@@ -10,10 +10,10 @@
 import 'util.dart';
 
 Future<Map<String, dynamic>> runAndCollect(String scriptPath,
-    {List<String> scriptArgs,
+    {List<String>? scriptArgs,
     bool checked = false,
     bool includeDart = false,
-    Duration timeout}) async {
+    Duration? timeout}) async {
   final dartArgs = [
     '--enable-vm-service',
     '--pause_isolates_on_exit',
@@ -47,7 +47,7 @@
     coverage = await collect(serviceUri, true, true, includeDart, <String>{},
         timeout: timeout);
   } finally {
-    await process.stderr.drain<List<int>>();
+    await process.stderr.drain();
   }
   final exitStatus = await process.exitCode;
   if (exitStatus != 0) {
diff --git a/pkgs/coverage/lib/src/util.dart b/pkgs/coverage/lib/src/util.dart
index bd375ea..45b2b9c 100644
--- a/pkgs/coverage/lib/src/util.dart
+++ b/pkgs/coverage/lib/src/util.dart
@@ -8,10 +8,10 @@
 /// Retries the specified function with the specified interval and returns
 /// the result on successful completion.
 Future<dynamic> retry(Future Function() f, Duration interval,
-    {Duration timeout}) async {
+    {Duration? timeout}) async {
   var keepGoing = true;
 
-  Future<dynamic> _withTimeout(Future Function() f, {Duration duration}) {
+  Future<dynamic> _withTimeout(Future Function() f, {Duration? duration}) {
     if (duration == null) {
       return f();
     }
@@ -41,7 +41,7 @@
 /// Scrapes and returns the observatory URI from a string, or null if not found.
 ///
 /// Potentially useful as a means to extract it from log statements.
-Uri extractObservatoryUri(String str) {
+Uri? extractObservatoryUri(String str) {
   const kObservatoryListening = 'Observatory listening on ';
   final msgPos = str.indexOf(kObservatoryListening);
   if (msgPos == -1) return null;
@@ -135,7 +135,7 @@
 /// ]
 /// ```
 ///
-List<List<int>> getIgnoredLines(List<String> lines) {
+List<List<int>> getIgnoredLines(List<String>? lines) {
   final ignoredLines = <List<int>>[];
   if (lines == null) return ignoredLines;
 
diff --git a/pkgs/coverage/pubspec.yaml b/pkgs/coverage/pubspec.yaml
index 72e9602..63d7c4f 100644
--- a/pkgs/coverage/pubspec.yaml
+++ b/pkgs/coverage/pubspec.yaml
@@ -1,24 +1,27 @@
 name: coverage
-version: 0.15.2
+version: 1.0.0
 description: Coverage data manipulation and formatting
 homepage: https://github.com/dart-lang/coverage
 
 environment:
-  sdk: '>=2.7.0 <3.0.0'
+  sdk: '>=2.12.0-0 <3.0.0'
 
 dependencies:
-  args: '>=1.4.0 <3.0.0'
-  logging: '>=0.9.0 <2.0.0'
-  package_config: '>=1.9.0 <3.0.0'
-  path: '>=0.9.0 <2.0.0'
-  source_maps: ^0.10.8
-  stack_trace: ^1.3.0
-  vm_service: '>=1.0.0 <7.0.0'
-
+  args: ^2.0.0
+  logging: ^1.0.0
+  package_config: ^2.0.0
+  path: ^1.8.0
+  source_maps: ^0.10.10
+  stack_trace: ^1.10.0
+  vm_service: ^6.1.0
 dev_dependencies:
-  pedantic: ^1.0.0
-  test: ^1.16.0-nullsafety.4
-  test_descriptor: ^2.0.0-nullsafety
+  pedantic: ^1.10.0
+  test: ^1.16.0
+  test_descriptor: ^2.0.0
 executables:
   collect_coverage:
   format_coverage:
+
+dependency_overrides:
+  test: ^1.16.0
+  test_core: ^0.3.14
diff --git a/pkgs/coverage/test/chrome_test.dart b/pkgs/coverage/test/chrome_test.dart
index b9c6a0e..2c378e7 100644
--- a/pkgs/coverage/test/chrome_test.dart
+++ b/pkgs/coverage/test/chrome_test.dart
@@ -18,7 +18,7 @@
   return File('test/test_files/main_test.js.map').readAsString();
 }
 
-Future<String> sourceProvider(String scriptId) async {
+Future<String?> sourceProvider(String scriptId) async {
   if (scriptId != mainScriptId) return null;
   return File('test/test_files/main_test.js').readAsString();
 }
diff --git a/pkgs/coverage/test/collect_coverage_api_test.dart b/pkgs/coverage/test/collect_coverage_api_test.dart
index e20374e..49d4efb 100644
--- a/pkgs/coverage/test/collect_coverage_api_test.dart
+++ b/pkgs/coverage/test/collect_coverage_api_test.dart
@@ -18,11 +18,6 @@
 final _isolateLibFileUri = p.toUri(p.absolute(_isolateLibPath)).toString();
 
 void main() {
-  test('collect throws when serviceUri is null', () {
-    expect(() => collect(null, true, false, false, <String>{}),
-        throwsArgumentError);
-  });
-
   test('collect_coverage_api', () async {
     final json = await _collectCoverage();
     expect(json.keys, unorderedEquals(<String>['type', 'coverage']));
@@ -38,11 +33,11 @@
       return map;
     });
 
-    for (var sampleCoverageData in sources[_sampleAppFileUri]) {
+    for (var sampleCoverageData in sources[_sampleAppFileUri]!) {
       expect(sampleCoverageData['hits'], isNotNull);
     }
 
-    for (var sampleCoverageData in sources[_isolateLibFileUri]) {
+    for (var sampleCoverageData in sources[_isolateLibFileUri]!) {
       expect(sampleCoverageData['hits'], isNotEmpty);
     }
   });
@@ -77,13 +72,13 @@
     final coverage = json['coverage'] as List<Map<String, dynamic>>;
     expect(coverage, isNotEmpty);
 
-    final testAppCoverage = _getScriptCoverage(coverage, 'test_app.dart');
+    final testAppCoverage = _getScriptCoverage(coverage, 'test_app.dart')!;
     var hits = testAppCoverage['hits'] as List<int>;
     _expectHitCount(hits, 44, 0);
     _expectHitCount(hits, 48, 0);
 
     final isolateCoverage =
-        _getScriptCoverage(coverage, 'test_app_isolate.dart');
+        _getScriptCoverage(coverage, 'test_app_isolate.dart')!;
     hits = isolateCoverage['hits'] as List<int>;
     _expectHitCount(hits, 11, 1);
     _expectHitCount(hits, 18, 1);
@@ -91,8 +86,7 @@
 }
 
 Future<Map<String, dynamic>> _collectCoverage(
-    {Set<String> scopedOutput, bool isolateIds = false}) async {
-  scopedOutput ??= <String>{};
+    {Set<String> scopedOutput = const {}, bool isolateIds = false}) async {
   final openPort = await getOpenPort();
 
   // run the sample app, with the right flags
@@ -126,7 +120,7 @@
 
 // Returns the first coverage hitmap for the script with with the specified
 // script filename, ignoring leading path.
-Map<String, dynamic> _getScriptCoverage(
+Map<String, dynamic>? _getScriptCoverage(
     List<Map<String, dynamic>> coverage, String filename) {
   for (var isolateCoverage in coverage) {
     final script = Uri.parse(isolateCoverage['script']['uri'] as String);
diff --git a/pkgs/coverage/test/collect_coverage_test.dart b/pkgs/coverage/test/collect_coverage_test.dart
index bd2b234..0407389 100644
--- a/pkgs/coverage/test/collect_coverage_test.dart
+++ b/pkgs/coverage/test/collect_coverage_test.dart
@@ -111,7 +111,7 @@
   });
 }
 
-String _coverageData;
+String? _coverageData;
 
 Future<String> _getCoverageResult() async =>
     _coverageData ??= await _collectCoverage();
@@ -158,7 +158,7 @@
   }
 
   await sampleProcess.exitCode;
-  await sampleProcess.stderr.drain<List<int>>();
+  await sampleProcess.stderr.drain();
 
   return toolResult.stdout as String;
 }
diff --git a/pkgs/coverage/test/format_coverage_test.dart b/pkgs/coverage/test/format_coverage_test.dart
index f791df3..dd834b6 100644
--- a/pkgs/coverage/test/format_coverage_test.dart
+++ b/pkgs/coverage/test/format_coverage_test.dart
@@ -6,7 +6,7 @@
 import '../bin/format_coverage.dart';
 
 void main() {
-  Directory testDir;
+  late Directory testDir;
   setUp(() {
     testDir = Directory.systemTemp.createTempSync('coverage_test_temp');
   });
diff --git a/pkgs/coverage/test/lcov_test.dart b/pkgs/coverage/test/lcov_test.dart
index b90ea99..1399820 100644
--- a/pkgs/coverage/test/lcov_test.dart
+++ b/pkgs/coverage/test/lcov_test.dart
@@ -111,7 +111,7 @@
       final hitLineRegexp = RegExp(r'\s+(\d+)\|  return a \+ b;');
       final match = hitLineRegexp.allMatches(res).single;
 
-      final hitCount = int.parse(match[1]);
+      final hitCount = int.parse(match[1]!);
       expect(hitCount, greaterThanOrEqualTo(1));
     });
 
@@ -186,6 +186,6 @@
     throw ProcessException(
         'dart', sampleAppArgs, 'Fatal error. Exit code: $exitCode', exitCode);
   }
-  await sampleProcess.stderr.drain<List<int>>();
+  await sampleProcess.stderr.drain();
   return hitMap;
 }
diff --git a/pkgs/coverage/test/test_files/test_app.dart b/pkgs/coverage/test/test_files/test_app.dart
index 1677fbe..79c5f2a 100644
--- a/pkgs/coverage/test/test_files/test_app.dart
+++ b/pkgs/coverage/test/test_files/test_app.dart
@@ -29,7 +29,7 @@
   print('isolateId = $isolateID');
 
   isolate.addOnExitListener(port.sendPort);
-  isolate.resume(isolate.pauseCapability);
+  isolate.resume(isolate.pauseCapability!);
 
   final value = await port.first as int;
   if (value != 3) {