Merge branch 'master' into null_safety
diff --git a/.travis.yml b/.travis.yml
index f3c200d..7d59ee7 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,29 +1,33 @@
 language: dart
 
 dart:
-  - dev
-  - 2.0.0
+ - dev
 
-dart_task:
-  - test: -p vm
-  - test: -p chrome
-  - dartanalyzer
-
-matrix:
+jobs:
   include:
-    # Only validate formatting using the dev release
-    - dart: dev
-      dart_task: dartfmt
-    - dart: dev
-      dart_task:
-        dartanalyzer: --fatal-infos --fatal-warnings .
-    - dart: 2.0.0
-      dart_task:
-        dartanalyzer: --fatal-warnings .
+    - stage: analyze_and_format
+      name: "Analyzer"
+      dart: dev
+      os: linux
+      script: dartanalyzer --enable-experiment=non-nullable --fatal-warnings --fatal-infos .
+    - stage: analyze_and_format
+      name: "Format"
+      dart: dev
+      os: linux
+      script: dartfmt -n --set-exit-if-changed .
+    - stage: test
+      name: "Vm Tests"
+      dart: dev
+      os: linux
+      script: pub run --enable-experiment=non-nullable test -p vm
+
+stages:
+  - analyze_and_format
+  - test
 
 # Only building master means that we don't run two builds for each pull request.
 branches:
-  only: [master]
+  only: [master, null_safety]
 
 cache:
   directories:
diff --git a/analysis_options.yaml b/analysis_options.yaml
index 70216af..3d96bfc 100644
--- a/analysis_options.yaml
+++ b/analysis_options.yaml
@@ -1,5 +1,7 @@
 include: package:pedantic/analysis_options.yaml
 analyzer:
+  enable-experiment:
+    - non-nullable
   strong-mode:
     implicit-casts: false
   errors:
diff --git a/lib/src/chain.dart b/lib/src/chain.dart
index 6b2eec5..ba6fa40 100644
--- a/lib/src/chain.dart
+++ b/lib/src/chain.dart
@@ -47,8 +47,8 @@
   final List<Trace> traces;
 
   /// The [StackZoneSpecification] for the current zone.
-  static StackZoneSpecification get _currentSpec =>
-      Zone.current[_specKey] as StackZoneSpecification;
+  static StackZoneSpecification? get _currentSpec =>
+      Zone.current[_specKey] as StackZoneSpecification?;
 
   /// If [when] is `true`, runs [callback] in a [Zone] in which the current
   /// stack chain is tracked and automatically associated with (most) errors.
@@ -73,7 +73,7 @@
   ///
   /// If [callback] returns a value, it will be returned by [capture] as well.
   static T capture<T>(T Function() callback,
-      {void Function(Object error, Chain) onError,
+      {void Function(Object error, Chain)? onError,
       bool when = true,
       bool errorZone = true}) {
     if (!errorZone && onError != null) {
@@ -82,15 +82,17 @@
     }
 
     if (!when) {
-      void Function(Object, StackTrace) newOnError;
+      void Function(Object, StackTrace)? newOnError;
       if (onError != null) {
-        newOnError = (error, stackTrace) {
+        void wrappedOnError(Object error, StackTrace? stackTrace) {
           onError(
               error,
               stackTrace == null
                   ? Chain.current()
                   : Chain.forTrace(stackTrace));
-        };
+        }
+
+        newOnError = wrappedOnError;
       }
 
       return runZoned(callback, onError: newOnError);
@@ -100,10 +102,15 @@
     return runZoned(() {
       try {
         return callback();
-      } catch (error, stackTrace) {
+      } on Object catch (error, stackTrace) {
         // TODO(nweiz): Don't special-case this when issue 19566 is fixed.
         Zone.current.handleUncaughtError(error, stackTrace);
-        return null;
+
+        // If the expected return type of capture() is not nullable, this will
+        // throw a cast exception. But the only other alternative is to throw
+        // some other exception. Casting null to T at least lets existing uses
+        // where T is a nullable type continue to work.
+        return null as T;
       }
     },
         zoneSpecification: spec.toSpec(),
@@ -138,7 +145,7 @@
   /// If this is called outside of a [capture] zone, it just returns a
   /// single-trace chain.
   factory Chain.current([int level = 0]) {
-    if (_currentSpec != null) return _currentSpec.currentChain(level + 1);
+    if (_currentSpec != null) return _currentSpec!.currentChain(level + 1);
 
     var chain = Chain.forTrace(StackTrace.current);
     return LazyChain(() {
@@ -160,7 +167,7 @@
   /// If [trace] is already a [Chain], it will be returned as-is.
   factory Chain.forTrace(StackTrace trace) {
     if (trace is Chain) return trace;
-    if (_currentSpec != null) return _currentSpec.chainFor(trace);
+    if (_currentSpec != null) return _currentSpec!.chainFor(trace);
     if (trace is Trace) return Chain([trace]);
     return LazyChain(() => Chain.parse(trace.toString()));
   }
diff --git a/lib/src/frame.dart b/lib/src/frame.dart
index 7ffc05f..18e4f98 100644
--- a/lib/src/frame.dart
+++ b/lib/src/frame.dart
@@ -79,18 +79,18 @@
   ///
   /// This can be null, indicating that the line number is unknown or
   /// unimportant.
-  final int line;
+  final int? line;
 
   /// The column number of the code location.
   ///
   /// This can be null, indicating that the column number is unknown or
   /// unimportant.
-  final int column;
+  final int? column;
 
   /// The name of the member in which the code location occurs.
   ///
   /// Anonymous closures are represented as `<fn>` in this member string.
-  final String member;
+  final String? member;
 
   /// Whether this stack frame comes from the Dart core libraries.
   bool get isCore => uri.scheme == 'dart';
@@ -107,7 +107,7 @@
 
   /// Returns the name of the package this stack frame comes from, or `null` if
   /// this stack frame doesn't come from a `package:` URL.
-  String get package {
+  String? get package {
     if (uri.scheme != 'package') return null;
     return uri.path.split('/').first;
   }
@@ -146,14 +146,14 @@
 
         // Get the pieces out of the regexp match. Function, URI and line should
         // always be found. The column is optional.
-        var member = match[1]
+        var member = match[1]!
             .replaceAll(_asyncBody, '<async>')
             .replaceAll('<anonymous closure>', '<fn>');
-        var uri = match[2].startsWith('<data:')
+        var uri = match[2]!.startsWith('<data:')
             ? Uri.dataFromString('')
-            : Uri.parse(match[2]);
+            : Uri.parse(match[2]!);
 
-        var lineAndColumn = match[3].split(':');
+        var lineAndColumn = match[3]!.split(':');
         var line =
             lineAndColumn.length > 1 ? int.parse(lineAndColumn[1]) : null;
         var column =
@@ -171,7 +171,7 @@
         Frame parseLocation(String location, String member) {
           var evalMatch = _v8EvalLocation.firstMatch(location);
           while (evalMatch != null) {
-            location = evalMatch[1];
+            location = evalMatch[1]!;
             evalMatch = _v8EvalLocation.firstMatch(location);
           }
 
@@ -182,8 +182,8 @@
           var urlMatch = _v8UrlLocation.firstMatch(location);
           if (urlMatch == null) return UnparsedFrame(frame);
 
-          final uri = _uriOrPathToUri(urlMatch[1]);
-          final line = int.parse(urlMatch[2]);
+          final uri = _uriOrPathToUri(urlMatch[1]!);
+          final line = int.parse(urlMatch[2]!);
           final column = urlMatch[3] != null ? int.parse(urlMatch[3]) : null;
           return Frame(uri, line, column, member);
         }
@@ -194,15 +194,15 @@
           // lists anonymous functions within eval as "<anonymous>", while IE10
           // lists them as "Anonymous function".
           return parseLocation(
-              match[2],
-              match[1]
+              match[2]!,
+              match[1]!
                   .replaceAll('<anonymous>', '<fn>')
                   .replaceAll('Anonymous function', '<fn>')
                   .replaceAll('(anonymous function)', '<fn>'));
         } else {
           // The second form looks like " at LOCATION", and is used for
           // anonymous functions.
-          return parseLocation(match[3], '<fn>');
+          return parseLocation(match[3]!, '<fn>');
         }
       });
 
@@ -224,9 +224,9 @@
       _catchFormatException(frame, () {
         final match = _firefoxEvalLocation.firstMatch(frame);
         if (match == null) return UnparsedFrame(frame);
-        var member = match[1].replaceAll('/<', '');
-        final uri = _uriOrPathToUri(match[2]);
-        final line = int.parse(match[3]);
+        var member = match[1]!.replaceAll('/<', '');
+        final uri = _uriOrPathToUri(match[2]!);
+        final line = int.parse(match[3]!);
         if (member.isEmpty || member == 'anonymous') {
           member = '<fn>';
         }
@@ -238,18 +238,17 @@
         var match = _firefoxSafariFrame.firstMatch(frame);
         if (match == null) return UnparsedFrame(frame);
 
-        if (match[3].contains(' line ')) {
+        if (match[3]!.contains(' line ')) {
           return Frame._parseFirefoxEval(frame);
         }
 
         // Normally this is a URI, but in a jsshell trace it can be a path.
-        var uri = _uriOrPathToUri(match[3]);
+        var uri = _uriOrPathToUri(match[3]!);
 
-        String member;
-        if (match[1] != null) {
-          member = match[1];
+        var member = match[1];
+        if (member != null) {
           member +=
-              List.filled('/'.allMatches(match[2]).length, '.<fn>').join();
+              List.filled('/'.allMatches(match[2]!).length, '.<fn>').join();
           if (member == '') member = '<fn>';
 
           // Some Firefox members have initial dots. We remove them for
@@ -259,9 +258,9 @@
           member = '<fn>';
         }
 
-        var line = match[4] == '' ? null : int.parse(match[4]);
+        var line = match[4] == '' ? null : int.parse(match[4]!);
         var column =
-            match[5] == null || match[5] == '' ? null : int.parse(match[5]);
+            match[5] == null || match[5] == '' ? null : int.parse(match[5]!);
         return Frame(uri, line, column, member);
       });
 
@@ -288,15 +287,15 @@
         // them.
         var uri = match[1] == 'data:...'
             ? Uri.dataFromString('')
-            : Uri.parse(match[1]);
-        // If there's no scheme, this is a relative URI. We should interpret it
-        // as relative to the current working directory.
+            : Uri.parse(match[1]!);
+        // If there's no scheme, this is a relative URI. We should interpret it as
+        // relative to the current working directory.
         if (uri.scheme == '') {
           uri = path.toUri(path.absolute(path.fromUri(uri)));
         }
 
-        var line = match[2] == null ? null : int.parse(match[2]);
-        var column = match[3] == null ? null : int.parse(match[3]);
+        var line = match[2] == null ? null : int.parse(match[2]!);
+        var column = match[3] == null ? null : int.parse(match[3]!);
         return Frame(uri, line, column, match[4]);
       });
 
diff --git a/lib/src/lazy_chain.dart b/lib/src/lazy_chain.dart
index 12dbace..e2f64a7 100644
--- a/lib/src/lazy_chain.dart
+++ b/lib/src/lazy_chain.dart
@@ -15,12 +15,10 @@
 /// necessary.
 class LazyChain implements Chain {
   final ChainThunk _thunk;
-  Chain _inner;
+  late final Chain _chain = _thunk();
 
   LazyChain(this._thunk);
 
-  Chain get _chain => _inner ??= _thunk();
-
   @override
   List<Trace> get traces => _chain.traces;
   @override
diff --git a/lib/src/lazy_trace.dart b/lib/src/lazy_trace.dart
index 0fa2af1..3ecaa2d 100644
--- a/lib/src/lazy_trace.dart
+++ b/lib/src/lazy_trace.dart
@@ -13,12 +13,10 @@
 /// necessary.
 class LazyTrace implements Trace {
   final TraceThunk _thunk;
-  Trace _inner;
+  late final Trace _trace = _thunk();
 
   LazyTrace(this._thunk);
 
-  Trace get _trace => _inner ??= _thunk();
-
   @override
   List<Frame> get frames => _trace.frames;
   @override
diff --git a/lib/src/stack_zone_specification.dart b/lib/src/stack_zone_specification.dart
index f721e94..13cdf27 100644
--- a/lib/src/stack_zone_specification.dart
+++ b/lib/src/stack_zone_specification.dart
@@ -53,10 +53,10 @@
   ///
   /// If this is null, that indicates that any unhandled errors should be passed
   /// to the parent zone.
-  final void Function(Object error, Chain) _onError;
+  final void Function(Object error, Chain)? _onError;
 
   /// The most recent node of the current stack chain.
-  _Node _currentNode;
+  _Node? _currentNode;
 
   /// Whether this is an error zone.
   final bool _errorZone;
@@ -86,7 +86,7 @@
   /// The first stack trace in the returned chain will always be [trace]
   /// (converted to a [Trace] if necessary). If there is no chain associated
   /// with [trace], this just returns a single-trace chain containing [trace].
-  Chain chainFor(StackTrace trace) {
+  Chain chainFor(StackTrace? trace) {
     if (trace is Chain) return trace;
     trace ??= StackTrace.current;
 
@@ -96,11 +96,11 @@
       // [Chain.capture] and we should fall back to the VM's stack chaining. We
       // can't use [Chain.from] here because it'll just call [chainFor] again.
       if (trace is Trace) return Chain([trace]);
-      return LazyChain(() => Chain.parse(trace.toString()));
+      return LazyChain(() => Chain.parse(trace!.toString()));
     } else {
       if (trace is! Trace) {
-        var original = trace;
-        trace = LazyTrace(() => Trace.parse(_trimVMChain(original)));
+        StackTrace? original = trace;
+        trace = LazyTrace(() => Trace.parse(_trimVMChain(original!)));
       }
 
       return _Node(trace, previous).toChain();
@@ -111,7 +111,7 @@
   /// [f] is run.
   ZoneCallback<R> _registerCallback<R>(
       Zone self, ZoneDelegate parent, Zone zone, R Function() f) {
-    if (f == null || _disabled) return parent.registerCallback(zone, f);
+    if (_disabled) return parent.registerCallback(zone, f);
     var node = _createNode(1);
     return parent.registerCallback(zone, () => _run(f, node));
   }
@@ -120,7 +120,7 @@
   /// [f] is run.
   ZoneUnaryCallback<R, T> _registerUnaryCallback<R, T>(
       Zone self, ZoneDelegate parent, Zone zone, R Function(T) f) {
-    if (f == null || _disabled) return parent.registerUnaryCallback(zone, f);
+    if (_disabled) return parent.registerUnaryCallback(zone, f);
     var node = _createNode(1);
     return parent.registerUnaryCallback(zone, (arg) {
       return _run(() => f(arg), node);
@@ -131,7 +131,7 @@
   /// [f] is run.
   ZoneBinaryCallback<R, T1, T2> _registerBinaryCallback<R, T1, T2>(
       Zone self, ZoneDelegate parent, Zone zone, R Function(T1, T2) f) {
-    if (f == null || _disabled) return parent.registerBinaryCallback(zone, f);
+    if (_disabled) return parent.registerBinaryCallback(zone, f);
 
     var node = _createNode(1);
     return parent.registerBinaryCallback(zone, (arg1, arg2) {
@@ -141,8 +141,8 @@
 
   /// Looks up the chain associated with [stackTrace] and passes it either to
   /// [_onError] or [parent]'s error handler.
-  void _handleUncaughtError(
-      Zone self, ZoneDelegate parent, Zone zone, error, StackTrace stackTrace) {
+  void _handleUncaughtError(Zone self, ZoneDelegate parent, Zone zone,
+      Object error, StackTrace stackTrace) {
     if (_disabled) {
       parent.handleUncaughtError(zone, error, stackTrace);
       return;
@@ -157,8 +157,10 @@
     // TODO(nweiz): Currently this copies a lot of logic from [runZoned]. Just
     // allow [runBinary] to throw instead once issue 18134 is fixed.
     try {
-      self.parent.runBinary(_onError, error, stackChain);
-    } catch (newError, newStackTrace) {
+      // TODO(rnystrom): Is the null-assertion correct here? It is nullable in
+      // Zone. Should we check for that here?
+      self.parent!.runBinary(_onError!, error, stackChain);
+    } on Object catch (newError, newStackTrace) {
       if (identical(newError, error)) {
         parent.handleUncaughtError(zone, error, stackChain);
       } else {
@@ -169,8 +171,8 @@
 
   /// Attaches the current stack chain to [stackTrace], replacing it if
   /// necessary.
-  AsyncError _errorCallback(Zone self, ZoneDelegate parent, Zone zone,
-      Object error, StackTrace stackTrace) {
+  AsyncError? _errorCallback(Zone self, ZoneDelegate parent, Zone zone,
+      Object error, StackTrace? stackTrace) {
     if (_disabled) return parent.errorCallback(zone, error, stackTrace);
 
     // Go up two levels to get through [_CustomZone.errorCallback].
@@ -217,15 +219,15 @@
 
   /// Like [new Trace.current], but if the current stack trace has VM chaining
   /// enabled, this only returns the innermost sub-trace.
-  Trace _currentTrace([int level]) {
-    level ??= 0;
+  Trace _currentTrace([int? level]) {
     var stackTrace = StackTrace.current;
     return LazyTrace(() {
       var text = _trimVMChain(stackTrace);
       var trace = Trace.parse(text);
       // JS includes a frame for the call to StackTrace.current, but the VM
       // doesn't, so we skip an extra frame in a JS context.
-      return Trace(trace.frames.skip(level + (inJS ? 2 : 1)), original: text);
+      return Trace(trace.frames.skip((level ?? 0) + (inJS ? 2 : 1)),
+          original: text);
     });
   }
 
@@ -244,14 +246,14 @@
   final Trace trace;
 
   /// The previous node in the chain.
-  final _Node previous;
+  final _Node? previous;
 
   _Node(StackTrace trace, [this.previous]) : trace = Trace.from(trace);
 
   /// Converts this to a [Chain].
   Chain toChain() {
     var nodes = <Trace>[];
-    var node = this;
+    _Node? node = this;
     while (node != null) {
       nodes.add(node.trace);
       node = node.previous;
diff --git a/lib/src/trace.dart b/lib/src/trace.dart
index 96902a1..dc7fd5d 100644
--- a/lib/src/trace.dart
+++ b/lib/src/trace.dart
@@ -107,13 +107,6 @@
   /// If [trace] is a native [StackTrace], its data will be parsed out; if it's
   /// a [Trace], it will be returned as-is.
   factory Trace.from(StackTrace trace) {
-    // Normally explicitly validating null arguments is bad Dart style, but here
-    // the natural failure will only occur when the LazyTrace is materialized,
-    // and we want to provide an error that's more local to the actual problem.
-    if (trace == null) {
-      throw ArgumentError('Cannot create a Trace from null.');
-    }
-
     if (trace is Trace) return trace;
     if (trace is Chain) return trace.toTrace();
     return LazyTrace(() => Trace.parse(trace.toString()));
@@ -240,9 +233,9 @@
             original: trace);
 
   /// Returns a new [Trace] comprised of [frames].
-  Trace(Iterable<Frame> frames, {String original})
+  Trace(Iterable<Frame> frames, {String? original})
       : frames = List<Frame>.unmodifiable(frames),
-        original = StackTrace.fromString(original);
+        original = StackTrace.fromString(original ?? '');
 
   /// Returns a VM-style [StackTrace] object.
   ///
@@ -296,7 +289,7 @@
         // just get rid of them.
         // TODO(nweiz): Get rid of this logic some time after issue 22009 is
         // fixed.
-        if (!frame.member.contains('<async>')) return false;
+        if (!frame.member!.contains('<async>')) return false;
         return frame.line == null;
       };
     }
diff --git a/lib/src/unparsed_frame.dart b/lib/src/unparsed_frame.dart
index 31d0179..27e97f6 100644
--- a/lib/src/unparsed_frame.dart
+++ b/lib/src/unparsed_frame.dart
@@ -11,15 +11,15 @@
   @override
   final Uri uri = Uri(path: 'unparsed');
   @override
-  final int line = null;
+  final int? line = null;
   @override
-  final int column = null;
+  final int? column = null;
   @override
   final bool isCore = false;
   @override
   final String library = 'unparsed';
   @override
-  final String package = null;
+  final String? package = null;
   @override
   final String location = 'unparsed';
 
diff --git a/lib/src/vm_trace.dart b/lib/src/vm_trace.dart
index 3ba73b9..005b7af 100644
--- a/lib/src/vm_trace.dart
+++ b/lib/src/vm_trace.dart
@@ -20,7 +20,7 @@
     var i = 1;
     return frames.map((frame) {
       var number = '#${i++}'.padRight(8);
-      var member = frame.member
+      var member = frame.member!
           .replaceAllMapped(RegExp(r'[^.]+\.<async>'),
               (match) => '${match[1]}.<${match[1]}_async_body>')
           .replaceAll('<fn>', '<anonymous closure>');
diff --git a/pubspec.yaml b/pubspec.yaml
index 147f7cc..6dbe934 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,11 +1,16 @@
 name: stack_trace
-version: 1.9.5
-
+# TODO(rnystrom): The version should be 2.0.0-dev, but doing so causes pub get
+# to fail since this depends on test, which has a cyclic dependency back onto
+# stack_trace. There are no versions of test that support stack_trace 2.0.0
+# yet.
+version: 1.10.0-dev
+# TODO(rnystrom): Remove this line once the above version issue is fixed.
+publish_to: none
 description: A package for manipulating stack traces and printing them readably.
 homepage: https://github.com/dart-lang/stack_trace
 
 environment:
-  sdk: '>=2.0.0 <3.0.0'
+  sdk: '>=2.9.0-1 <3.0.0'
 
 dependencies:
   path: ^1.2.0
@@ -13,3 +18,73 @@
 dev_dependencies:
   pedantic: ^1.0.0
   test: '>=0.12.17 <2.0.0'
+
+dependency_overrides:
+  async:
+    git:
+      url: git://github.com/dart-lang/async.git
+      ref: null_safety
+  boolean_selector:
+    git:
+      url: git://github.com/dart-lang/boolean_selector.git
+      ref: null_safety
+  charcode:
+    git:
+      url: git://github.com/dart-lang/charcode.git
+      ref: null_safety
+  collection:
+    git:
+      url: git://github.com/dart-lang/collection.git
+      ref: null_safety
+  matcher:
+    git:
+      url: git://github.com/dart-lang/matcher.git
+      ref: null_safety
+  meta:
+    git:
+      url: git://github.com/dart-lang/sdk.git
+      ref: null_safety-pkgs
+      path: pkg/meta
+  path:
+    git:
+      url: git://github.com/dart-lang/path.git
+      ref: null_safety
+  pedantic:
+    git:
+      url: git://github.com/dart-lang/pedantic.git
+      ref: null_safety
+  pool:
+    git:
+      url: git://github.com/dart-lang/pool.git
+      ref: null_safety
+  source_span:
+    git:
+      url: git://github.com/dart-lang/source_span.git
+      ref: null_safety
+  stream_channel:
+    git:
+      url: git://github.com/dart-lang/stream_channel.git
+      ref: null_safety
+  string_scanner:
+    git:
+      url: git://github.com/dart-lang/string_scanner.git
+      ref: null_safety
+  term_glyph:
+    git:
+      url: git://github.com/dart-lang/term_glyph.git
+      ref: null_safety
+  test_api:
+    git:
+      url: git://github.com/dart-lang/test.git
+      ref: null_safety
+      path: pkgs/test_api
+  test_core:
+    git:
+      url: git://github.com/dart-lang/test.git
+      ref: null_safety
+      path: pkgs/test_core
+  test:
+    git:
+      url: git://github.com/dart-lang/test.git
+      ref: null_safety
+      path: pkgs/test
diff --git a/test/chain/chain_test.dart b/test/chain/chain_test.dart
index b6ad33e..1e466ac 100644
--- a/test/chain/chain_test.dart
+++ b/test/chain/chain_test.dart
@@ -44,7 +44,7 @@
         return Future.error('oh no');
       }, onError: expectAsync2((error, chain) {
         expect(error, equals('oh no'));
-        expect(chain, TypeMatcher<Chain>());
+        expect(chain, isA<Chain>());
       }));
     });
 
@@ -54,7 +54,7 @@
         future.then(expectAsync1((_) {}, count: 0));
       }, onError: expectAsync2((error, chain) {
         expect(error, equals('oh no'));
-        expect(chain, TypeMatcher<Chain>());
+        expect(chain, isA<Chain>());
       }));
     });
 
@@ -79,7 +79,7 @@
           return Future.error('oh no');
         }, onError: expectAsync2((error, chain) {
           expect(error, equals('oh no'));
-          expect(chain, TypeMatcher<Chain>());
+          expect(chain, isA<Chain>());
         }), when: false);
       });
 
diff --git a/test/chain/dart2js_test.dart b/test/chain/dart2js_test.dart
index 827c459..c0ec913 100644
--- a/test/chain/dart2js_test.dart
+++ b/test/chain/dart2js_test.dart
@@ -90,7 +90,7 @@
             expect(chain.traces, hasLength(2));
             completer.complete();
           }
-        } catch (error, stackTrace) {
+        } on Object catch (error, stackTrace) {
           completer.completeError(error, stackTrace);
         }
       });
@@ -151,10 +151,10 @@
       }, onError: (error, chain) {
         try {
           expect(error, equals('error'));
-          expect(chain, TypeMatcher<Chain>());
+          expect(chain, isA<Chain>());
           expect(chain.traces, hasLength(2));
           completer.complete();
-        } catch (error, stackTrace) {
+        } on Object catch (error, stackTrace) {
           completer.completeError(error, stackTrace);
         }
       });
@@ -171,10 +171,10 @@
     }, onError: (error, chain) {
       try {
         expect(error, equals('error'));
-        expect(chain, TypeMatcher<Chain>());
+        expect(chain, isA<Chain>());
         expect(chain.traces, hasLength(2));
         completer.complete();
-      } catch (error, stackTrace) {
+      } on Object catch (error, stackTrace) {
         completer.completeError(error, stackTrace);
       }
     });
@@ -305,7 +305,7 @@
     test(
         'called for an unregistered stack trace returns a chain wrapping that '
         'trace', () {
-      StackTrace trace;
+      late StackTrace trace;
       var chain = Chain.capture(() {
         try {
           throw 'error';
@@ -324,7 +324,7 @@
   test(
       'forTrace() outside of capture() returns a chain wrapping the given '
       'trace', () {
-    StackTrace trace;
+    late StackTrace trace;
     var chain = Chain.capture(() {
       try {
         throw 'error';
diff --git a/test/chain/utils.dart b/test/chain/utils.dart
index 0711108..af8e361 100644
--- a/test/chain/utils.dart
+++ b/test/chain/utils.dart
@@ -46,7 +46,7 @@
 /// Returns a Future that completes to an error using a completer.
 ///
 /// If [trace] is passed, it's used as the stack trace for the error.
-Future completerErrorFuture([StackTrace trace]) {
+Future completerErrorFuture([StackTrace? trace]) {
   var completer = Completer();
   completer.completeError('error', trace);
   return completer.future;
@@ -55,7 +55,7 @@
 /// Returns a Stream that emits an error using a controller.
 ///
 /// If [trace] is passed, it's used as the stack trace for the error.
-Stream controllerErrorStream([StackTrace trace]) {
+Stream controllerErrorStream([StackTrace? trace]) {
   var controller = StreamController();
   controller.addError('error', trace);
   return controller.stream;
diff --git a/test/chain/vm_test.dart b/test/chain/vm_test.dart
index 842f0a0..273276e 100644
--- a/test/chain/vm_test.dart
+++ b/test/chain/vm_test.dart
@@ -19,7 +19,7 @@
 void main() {
   group('capture() with onError catches exceptions', () {
     test('thrown synchronously', () async {
-      StackTrace vmTrace;
+      late StackTrace vmTrace;
       var chain = await captureFuture(() {
         try {
           throw 'error';
@@ -156,7 +156,7 @@
                 contains(frameMember(startsWith('inPeriodicTimer'))));
             completer.complete();
           }
-        } catch (error, stackTrace) {
+        } on Object catch (error, stackTrace) {
           completer.completeError(error, stackTrace);
         }
       });
@@ -246,11 +246,11 @@
       }, onError: (error, chain) {
         try {
           expect(error, equals('error'));
-          expect(chain, TypeMatcher<Chain>());
+          expect(chain, isA<Chain>());
           expect(chain.traces[1].frames,
               contains(frameMember(startsWith('inMicrotask'))));
           completer.complete();
-        } catch (error, stackTrace) {
+        } on Object catch (error, stackTrace) {
           completer.completeError(error, stackTrace);
         }
       });
@@ -267,11 +267,11 @@
     }, onError: (error, chain) {
       try {
         expect(error, equals('error'));
-        expect(chain, TypeMatcher<Chain>());
+        expect(chain, isA<Chain>());
         expect(chain.traces[1].frames,
             contains(frameMember(startsWith('inMicrotask'))));
         completer.complete();
-      } catch (error, stackTrace) {
+      } on Object catch (error, stackTrace) {
         completer.completeError(error, stackTrace);
       }
     });
@@ -481,7 +481,7 @@
       'chain', () {
     // Disable the test package's chain-tracking.
     return Chain.disable(() async {
-      StackTrace trace;
+      late StackTrace trace;
       await Chain.capture(() async {
         try {
           throw 'error';
diff --git a/test/frame_test.dart b/test/frame_test.dart
index 087b5b6..8788039 100644
--- a/test/frame_test.dart
+++ b/test/frame_test.dart
@@ -40,7 +40,7 @@
     });
 
     test('converts "<anonymous closure>" to "<fn>"', () {
-      String parsedMember(String member) =>
+      String? parsedMember(String member) =>
           Frame.parseVM('#0 $member (foo:0:0)').member;
 
       expect(parsedMember('Foo.<anonymous closure>'), equals('Foo.<fn>'));
@@ -210,7 +210,7 @@
     });
 
     test('converts "<anonymous>" to "<fn>"', () {
-      String parsedMember(String member) =>
+      String? parsedMember(String member) =>
           Frame.parseV8('    at $member (foo:0:0)').member;
 
       expect(parsedMember('Foo.<anonymous>'), equals('Foo.<fn>'));
@@ -646,6 +646,6 @@
 
 void expectIsUnparsed(Frame Function(String) constructor, String text) {
   var frame = constructor(text);
-  expect(frame, TypeMatcher<UnparsedFrame>());
+  expect(frame, isA<UnparsedFrame>());
   expect(frame.toString(), equals(text));
 }
diff --git a/test/trace_test.dart b/test/trace_test.dart
index ec7b074..a10c69d 100644
--- a/test/trace_test.dart
+++ b/test/trace_test.dart
@@ -6,7 +6,7 @@
 import 'package:stack_trace/stack_trace.dart';
 import 'package:test/test.dart';
 
-Trace getCurrentTrace([int level]) => Trace.current(level);
+Trace getCurrentTrace([int level = 0]) => Trace.current(level);
 
 Trace nestedGetCurrentTrace(int level) => getCurrentTrace(level);
 
@@ -366,7 +366,7 @@
 ''');
 
         var folded =
-            trace.foldFrames((frame) => frame.member.startsWith('foo'));
+            trace.foldFrames((frame) => frame.member!.startsWith('foo'));
         expect(folded.toString(), equals('''
 foo.dart 42:21                     notFoo
 foo.dart 1:100                     fooBottom
@@ -401,7 +401,7 @@
 ''');
 
           var folded = trace.foldFrames(
-              (frame) => frame.member.startsWith('foo'),
+              (frame) => frame.member!.startsWith('foo'),
               terse: true);
           expect(folded.toString(), equals('''
 foo.dart 42:21  notFoo
@@ -422,7 +422,7 @@
 ''');
 
           var folded = trace.foldFrames(
-              (frame) => frame.member.startsWith('foo'),
+              (frame) => frame.member!.startsWith('foo'),
               terse: true);
           expect(folded.toString(), equals('''
 foo.dart 42:21  notFoo
@@ -441,7 +441,7 @@
 ''');
 
           var folded = trace.foldFrames(
-              (frame) => frame.member.startsWith('foo'),
+              (frame) => frame.member!.startsWith('foo'),
               terse: true);
           expect(folded.toString(), equals('''
 package:foo     fooTop
diff --git a/test/vm_test.dart b/test/vm_test.dart
index 4ad3de2..c9f819a 100644
--- a/test/vm_test.dart
+++ b/test/vm_test.dart
@@ -18,14 +18,14 @@
 // The name of this (trivial) function is verified as part of the test
 StackTrace getStackTraceObject() => StackTrace.current;
 
-Frame getCaller([int level]) {
+Frame getCaller([int? level]) {
   if (level == null) return Frame.caller();
   return Frame.caller(level);
 }
 
 Frame nestedGetCaller(int level) => getCaller(level);
 
-Trace getCurrentTrace([int level]) => Trace.current(level);
+Trace getCurrentTrace([int level = 0]) => Trace.current(level);
 
 Trace nestedGetCurrentTrace(int level) => getCurrentTrace(level);
 
@@ -49,14 +49,14 @@
     test('.from handles a stack overflow trace correctly', () {
       void overflow() => overflow();
 
-      var trace;
+      late Trace? trace;
       try {
         overflow();
       } catch (_, stackTrace) {
         trace = Trace.from(stackTrace);
       }
 
-      expect(trace.frames.first.member, equals('main.<fn>.<fn>.overflow'));
+      expect(trace!.frames.first.member, equals('main.<fn>.<fn>.overflow'));
     });
 
     group('.current()', () {