Merge pull request dart-lang/yaml#94 from dart-lang/null_safety

migrate to nnbd
diff --git a/pkgs/yaml/.travis.yml b/pkgs/yaml/.travis.yml
index e982192..e0a5b90 100644
--- a/pkgs/yaml/.travis.yml
+++ b/pkgs/yaml/.travis.yml
@@ -1,7 +1,6 @@
 language: dart
 
 dart:
-  - 2.4.0
   - dev
 
 dart_task:
diff --git a/pkgs/yaml/CHANGELOG.md b/pkgs/yaml/CHANGELOG.md
index 4f63399..1cb12d6 100644
--- a/pkgs/yaml/CHANGELOG.md
+++ b/pkgs/yaml/CHANGELOG.md
@@ -1,5 +1,5 @@
-## 2.3.0-dev
-
+## 3.0.0-nullsafety
+* Updated to support 2.12.0 and null safety.
 * Allow `YamlNode`s to be wrapped with an optional `style` parameter.
 
 ## 2.2.1
diff --git a/pkgs/yaml/lib/src/event.dart b/pkgs/yaml/lib/src/event.dart
index c4f6a6d..e8879ee 100644
--- a/pkgs/yaml/lib/src/event.dart
+++ b/pkgs/yaml/lib/src/event.dart
@@ -26,7 +26,7 @@
   final FileSpan span;
 
   /// The document's `%YAML` directive, or `null` if there was none.
-  final VersionDirective versionDirective;
+  final VersionDirective? versionDirective;
 
   /// The document's `%TAG` directives, if any.
   final List<TagDirective> tagDirectives;
@@ -37,7 +37,7 @@
 
   DocumentStartEvent(this.span,
       {this.versionDirective,
-      List<TagDirective> tagDirectives,
+      List<TagDirective>? tagDirectives,
       this.isImplicit = true})
       : tagDirectives = tagDirectives ?? [];
 
@@ -81,10 +81,10 @@
 /// An event that can have associated anchor and tag properties.
 abstract class _ValueEvent implements Event {
   /// The name of the value's anchor, or `null` if it wasn't anchored.
-  String get anchor;
+  String? get anchor;
 
   /// The text of the value's tag, or `null` if it wasn't tagged.
-  String get tag;
+  String? get tag;
 
   @override
   String toString() {
@@ -102,9 +102,9 @@
   @override
   final FileSpan span;
   @override
-  final String anchor;
+  final String? anchor;
   @override
-  final String tag;
+  final String? tag;
 
   /// The contents of the scalar.
   final String value;
@@ -125,9 +125,9 @@
   @override
   final FileSpan span;
   @override
-  final String anchor;
+  final String? anchor;
   @override
-  final String tag;
+  final String? tag;
 
   /// The style of the collection in the original source.
   final CollectionStyle style;
@@ -142,9 +142,9 @@
   @override
   final FileSpan span;
   @override
-  final String anchor;
+  final String? anchor;
   @override
-  final String tag;
+  final String? tag;
 
   /// The style of the collection in the original source.
   final CollectionStyle style;
diff --git a/pkgs/yaml/lib/src/loader.dart b/pkgs/yaml/lib/src/loader.dart
index 54172b5..c60e673 100644
--- a/pkgs/yaml/lib/src/loader.dart
+++ b/pkgs/yaml/lib/src/loader.dart
@@ -32,17 +32,19 @@
   /// Creates a loader that loads [source].
   ///
   /// [sourceUrl] can be a String or a [Uri].
-  Loader(String source, {sourceUrl})
-      : _parser = Parser(source, sourceUrl: sourceUrl) {
-    var event = _parser.parse();
-    _span = event.span;
+  factory Loader(String source, {sourceUrl}) {
+    var parser = Parser(source, sourceUrl: sourceUrl);
+    var event = parser.parse();
     assert(event.type == EventType.streamStart);
+    return Loader._(parser, event.span);
   }
 
+  Loader._(this._parser, this._span);
+
   /// Loads the next document from the stream.
   ///
   /// If there are no more documents, returns `null`.
-  YamlDocument load() {
+  YamlDocument? load() {
     if (_parser.isDone) return null;
 
     var event = _parser.parse();
@@ -90,7 +92,7 @@
   }
 
   /// Registers an anchor.
-  void _registerAnchor(String anchor, YamlNode node) {
+  void _registerAnchor(String? anchor, YamlNode node) {
     if (anchor == null) return;
 
     // libyaml throws an error for duplicate anchors, but example 7.1 makes it
@@ -207,7 +209,7 @@
   ///
   /// If parsing fails, this returns `null`, indicating that the scalar should
   /// be parsed as a string.
-  YamlScalar _tryParseScalar(ScalarEvent scalar) {
+  YamlScalar? _tryParseScalar(ScalarEvent scalar) {
     // Quickly check for the empty string, which means null.
     var length = scalar.value.length;
     if (length == 0) return YamlScalar.internal(null, scalar);
@@ -239,7 +241,7 @@
   /// Parse a null scalar.
   ///
   /// Returns a Dart `null` if parsing fails.
-  YamlScalar _parseNull(ScalarEvent scalar) {
+  YamlScalar? _parseNull(ScalarEvent scalar) {
     switch (scalar.value) {
       case '':
       case 'null':
@@ -255,7 +257,7 @@
   /// Parse a boolean scalar.
   ///
   /// Returns `null` if parsing fails.
-  YamlScalar _parseBool(ScalarEvent scalar) {
+  YamlScalar? _parseBool(ScalarEvent scalar) {
     switch (scalar.value) {
       case 'true':
       case 'True':
@@ -273,7 +275,7 @@
   /// Parses a numeric scalar.
   ///
   /// Returns `null` if parsing fails.
-  YamlScalar _parseNumber(ScalarEvent scalar,
+  YamlScalar? _parseNumber(ScalarEvent scalar,
       {bool allowInt = true, bool allowFloat = true}) {
     var value = _parseNumberValue(scalar.value,
         allowInt: allowInt, allowFloat: allowFloat);
@@ -283,7 +285,7 @@
   /// Parses the value of a number.
   ///
   /// Returns the number if it's parsed successfully, or `null` if it's not.
-  num _parseNumberValue(String contents,
+  num? _parseNumberValue(String contents,
       {bool allowInt = true, bool allowFloat = true}) {
     assert(allowInt || allowFloat);
 
@@ -315,7 +317,7 @@
             secondChar >= $0 &&
             secondChar <= $9)) {
       // Try to parse an int or, failing that, a double.
-      num result;
+      num? result;
       if (allowInt) {
         // Pass "radix: 10" explicitly to ensure that "-0x10", which is valid
         // Dart but invalid YAML, doesn't get parsed.
diff --git a/pkgs/yaml/lib/src/parser.dart b/pkgs/yaml/lib/src/parser.dart
index 4c8aaaf..625abe6 100644
--- a/pkgs/yaml/lib/src/parser.dart
+++ b/pkgs/yaml/lib/src/parser.dart
@@ -130,7 +130,7 @@
   ///       DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END*
   ///       *************************
   Event _parseDocumentStart() {
-    var token = _scanner.peek();
+    var token = _scanner.peek()!;
 
     // libyaml requires any document beyond the first in the stream to have an
     // explicit document start indicator, but the spec allows it to be omitted
@@ -138,7 +138,7 @@
 
     // Parse extra document end indicators.
     while (token.type == TokenType.documentEnd) {
-      token = _scanner.advance();
+      token = _scanner.advance()!;
     }
 
     if (token.type != TokenType.versionDirective &&
@@ -163,7 +163,7 @@
     var pair = _processDirectives();
     var versionDirective = pair.first;
     var tagDirectives = pair.last;
-    token = _scanner.peek();
+    token = _scanner.peek()!;
     if (token.type != TokenType.documentStart) {
       throw YamlException('Expected document start.', token.span);
     }
@@ -183,7 +183,7 @@
   ///       DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END*
   ///                                 ***********
   Event _parseDocumentContent() {
-    var token = _scanner.peek();
+    var token = _scanner.peek()!;
 
     switch (token.type) {
       case TokenType.versionDirective:
@@ -209,7 +209,7 @@
     _tagDirectives.clear();
     _state = _State.DOCUMENT_START;
 
-    var token = _scanner.peek();
+    var token = _scanner.peek()!;
     if (token.type == TokenType.documentEnd) {
       _scanner.scan();
       return DocumentEndEvent(token.span, isImplicit: false);
@@ -246,7 +246,7 @@
   ///     flow_content         ::= flow_collection | SCALAR
   ///                                                ******
   Event _parseNode({bool block = false, bool indentlessSequence = false}) {
-    var token = _scanner.peek();
+    var token = _scanner.peek()!;
 
     if (token is AliasToken) {
       _scanner.scan();
@@ -254,40 +254,40 @@
       return AliasEvent(token.span, token.name);
     }
 
-    String anchor;
-    TagToken tagToken;
+    String? anchor;
+    TagToken? tagToken;
     var span = token.span.start.pointSpan();
     Token parseAnchor(AnchorToken token) {
       anchor = token.name;
       span = span.expand(token.span);
-      return _scanner.advance();
+      return _scanner.advance()!;
     }
 
     Token parseTag(TagToken token) {
       tagToken = token;
       span = span.expand(token.span);
-      return _scanner.advance();
+      return _scanner.advance()!;
     }
 
     if (token is AnchorToken) {
-      token = parseAnchor(token as AnchorToken);
-      if (token is TagToken) token = parseTag(token as TagToken);
+      token = parseAnchor(token);
+      if (token is TagToken) token = parseTag(token);
     } else if (token is TagToken) {
-      token = parseTag(token as TagToken);
-      if (token is AnchorToken) token = parseAnchor(token as AnchorToken);
+      token = parseTag(token);
+      if (token is AnchorToken) token = parseAnchor(token);
     }
 
-    String tag;
+    String? tag;
     if (tagToken != null) {
-      if (tagToken.handle == null) {
-        tag = tagToken.suffix;
+      if (tagToken!.handle == null) {
+        tag = tagToken!.suffix;
       } else {
-        var tagDirective = _tagDirectives[tagToken.handle];
+        var tagDirective = _tagDirectives[tagToken!.handle];
         if (tagDirective == null) {
-          throw YamlException('Undefined tag handle.', tagToken.span);
+          throw YamlException('Undefined tag handle.', tagToken!.span);
         }
 
-        tag = tagDirective.prefix + tagToken.suffix;
+        tag = tagDirective.prefix + (tagToken?.suffix ?? '');
       }
     }
 
@@ -345,11 +345,11 @@
   ///       BLOCK-SEQUENCE-START (BLOCK-ENTRY block_node?)* BLOCK-END
   ///       ********************  *********** *             *********
   Event _parseBlockSequenceEntry() {
-    var token = _scanner.peek();
+    var token = _scanner.peek()!;
 
     if (token.type == TokenType.blockEntry) {
       var start = token.span.start;
-      token = _scanner.advance();
+      token = _scanner.advance()!;
 
       if (token.type == TokenType.blockEntry ||
           token.type == TokenType.blockEnd) {
@@ -376,7 +376,7 @@
   ///     indentless_sequence  ::= (BLOCK-ENTRY block_node?)+
   ///                               *********** *
   Event _parseIndentlessSequenceEntry() {
-    var token = _scanner.peek();
+    var token = _scanner.peek()!;
 
     if (token.type != TokenType.blockEntry) {
       _state = _states.removeLast();
@@ -384,7 +384,7 @@
     }
 
     var start = token.span.start;
-    token = _scanner.advance();
+    token = _scanner.advance()!;
 
     if (token.type == TokenType.blockEntry ||
         token.type == TokenType.key ||
@@ -409,10 +409,10 @@
   ///                              BLOCK-END
   ///                              *********
   Event _parseBlockMappingKey() {
-    var token = _scanner.peek();
+    var token = _scanner.peek()!;
     if (token.type == TokenType.key) {
       var start = token.span.start;
-      token = _scanner.advance();
+      token = _scanner.advance()!;
 
       if (token.type == TokenType.key ||
           token.type == TokenType.value ||
@@ -454,7 +454,7 @@
   ///                              BLOCK-END
   ///
   Event _parseBlockMappingValue() {
-    var token = _scanner.peek();
+    var token = _scanner.peek()!;
 
     if (token.type != TokenType.value) {
       _state = _State.BLOCK_MAPPING_KEY;
@@ -462,7 +462,7 @@
     }
 
     var start = token.span.start;
-    token = _scanner.advance();
+    token = _scanner.advance()!;
     if (token.type == TokenType.key ||
         token.type == TokenType.value ||
         token.type == TokenType.blockEnd) {
@@ -489,7 +489,7 @@
   ///       *
   Event _parseFlowSequenceEntry({bool first = false}) {
     if (first) _scanner.scan();
-    var token = _scanner.peek();
+    var token = _scanner.peek()!;
 
     if (token.type != TokenType.flowSequenceEnd) {
       if (!first) {
@@ -499,7 +499,7 @@
               token.span.start.pointSpan());
         }
 
-        token = _scanner.advance();
+        token = _scanner.advance()!;
       }
 
       if (token.type == TokenType.key) {
@@ -523,7 +523,7 @@
   ///       flow_node | KEY flow_node? (VALUE flow_node?)?
   ///                   *** *
   Event _parseFlowSequenceEntryMappingKey() {
-    var token = _scanner.peek();
+    var token = _scanner.peek()!;
 
     if (token.type == TokenType.value ||
         token.type == TokenType.flowEntry ||
@@ -547,10 +547,10 @@
   ///       flow_node | KEY flow_node? (VALUE flow_node?)?
   ///                                   ***** *
   Event _parseFlowSequenceEntryMappingValue() {
-    var token = _scanner.peek();
+    var token = _scanner.peek()!;
 
     if (token.type == TokenType.value) {
-      token = _scanner.advance();
+      token = _scanner.advance()!;
       if (token.type != TokenType.flowEntry &&
           token.type != TokenType.flowSequenceEnd) {
         _states.add(_State.FLOW_SEQUENCE_ENTRY_MAPPING_END);
@@ -569,7 +569,7 @@
   ///                                                   *
   Event _parseFlowSequenceEntryMappingEnd() {
     _state = _State.FLOW_SEQUENCE_ENTRY;
-    return Event(EventType.mappingEnd, _scanner.peek().span.start.pointSpan());
+    return Event(EventType.mappingEnd, _scanner.peek()!.span.start.pointSpan());
   }
 
   /// Parses the productions:
@@ -587,7 +587,7 @@
   ///       *           *** *
   Event _parseFlowMappingKey({bool first = false}) {
     if (first) _scanner.scan();
-    var token = _scanner.peek();
+    var token = _scanner.peek()!;
 
     if (token.type != TokenType.flowMappingEnd) {
       if (!first) {
@@ -597,11 +597,11 @@
               token.span.start.pointSpan());
         }
 
-        token = _scanner.advance();
+        token = _scanner.advance()!;
       }
 
       if (token.type == TokenType.key) {
-        token = _scanner.advance();
+        token = _scanner.advance()!;
         if (token.type != TokenType.value &&
             token.type != TokenType.flowEntry &&
             token.type != TokenType.flowMappingEnd) {
@@ -628,7 +628,7 @@
   ///       flow_node | KEY flow_node? (VALUE flow_node?)?
   ///                *                  ***** *
   Event _parseFlowMappingValue({bool empty = false}) {
-    var token = _scanner.peek();
+    var token = _scanner.peek()!;
 
     if (empty) {
       _state = _State.FLOW_MAPPING_KEY;
@@ -636,7 +636,7 @@
     }
 
     if (token.type == TokenType.value) {
-      token = _scanner.advance();
+      token = _scanner.advance()!;
       if (token.type != TokenType.flowEntry &&
           token.type != TokenType.flowMappingEnd) {
         _states.add(_State.FLOW_MAPPING_KEY);
@@ -653,10 +653,10 @@
       ScalarEvent(location.pointSpan() as FileSpan, '', ScalarStyle.PLAIN);
 
   /// Parses directives.
-  Pair<VersionDirective, List<TagDirective>> _processDirectives() {
-    var token = _scanner.peek();
+  Pair<VersionDirective?, List<TagDirective>> _processDirectives() {
+    var token = _scanner.peek()!;
 
-    VersionDirective versionDirective;
+    VersionDirective? versionDirective;
     var tagDirectives = <TagDirective>[];
     while (token.type == TokenType.versionDirective ||
         token.type == TokenType.tagDirective) {
@@ -684,7 +684,7 @@
         tagDirectives.add(tagDirective);
       }
 
-      token = _scanner.advance();
+      token = _scanner.advance()!;
     }
 
     _appendTagDirective(TagDirective('!', '!'), token.span.start.pointSpan(),
diff --git a/pkgs/yaml/lib/src/scanner.dart b/pkgs/yaml/lib/src/scanner.dart
index 98d54df..942e578 100644
--- a/pkgs/yaml/lib/src/scanner.dart
+++ b/pkgs/yaml/lib/src/scanner.dart
@@ -139,7 +139,7 @@
   /// When a ":" is parsed and there's a simple key available, a [TokenType.key]
   /// token is inserted in [_tokens] before that key's token. This allows the
   /// parser to tell that the key is intended to be a mapping key.
-  final _simpleKeys = <_SimpleKey>[null];
+  final _simpleKeys = <_SimpleKey?>[null];
 
   /// The current indentation level.
   int get _indent => _indents.last;
@@ -306,13 +306,13 @@
   }
 
   /// Consumes the next token and returns the one after that.
-  Token advance() {
+  Token? advance() {
     scan();
     return peek();
   }
 
   /// Returns the next token without consuming it.
-  Token peek() {
+  Token? peek() {
     if (_streamEndProduced) return null;
     if (!_tokenAvailable) _fetchMoreTokens();
     return _tokens.first;
@@ -548,7 +548,7 @@
   /// [tokenNumber] is provided, the corresponding token will be replaced;
   /// otherwise, the token will be added at the end.
   void _rollIndent(int column, TokenType type, SourceLocation location,
-      {int tokenNumber}) {
+      {int? tokenNumber}) {
     if (!_inBlockContext) return;
     if (_indent != -1 && _indent >= column) return;
 
@@ -822,7 +822,7 @@
   ///     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   ///     %TAG    !yaml!  tag:yaml.org,2002:  \n
   ///     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-  Token _scanDirective() {
+  Token? _scanDirective() {
     var start = _scanner.state;
 
     // Eat '%'.
@@ -979,7 +979,7 @@
 
   /// Scans a [TokenType.tag] token.
   Token _scanTag() {
-    String handle;
+    String? handle;
     String suffix;
     var start = _scanner.state;
 
@@ -1052,14 +1052,14 @@
   /// [head] is the initial portion of the tag that's already been scanned.
   /// [flowSeparators] indicates whether the tag URI can contain flow
   /// separators.
-  String _scanTagUri({String head, bool flowSeparators = true}) {
+  String _scanTagUri({String? head, bool flowSeparators = true}) {
     var length = head == null ? 0 : head.length;
     var buffer = StringBuffer();
 
     // Copy the head if needed.
     //
     // Note that we don't copy the leading '!' character.
-    if (length > 1) buffer.write(head.substring(1));
+    if (length > 1) buffer.write(head!.substring(1));
 
     // The set of characters that may appear in URI is as follows:
     //
@@ -1287,7 +1287,7 @@
           var escapeStart = _scanner.state;
 
           // An escape sequence.
-          int codeLength;
+          int? codeLength;
           switch (_scanner.peekChar(1)) {
             case NUMBER_0:
               buffer.writeCharCode(NULL);
@@ -1324,7 +1324,7 @@
               // libyaml doesn't support an escaped forward slash, but it was
               // added in YAML 1.2. See section 5.7:
               // http://yaml.org/spec/1.2/spec.html#id2776092
-              buffer.writeCharCode(_scanner.peekChar(1));
+              buffer.writeCharCode(_scanner.peekChar(1)!);
               break;
             case LETTER_CAP_N:
               buffer.writeCharCode(NEL);
@@ -1658,7 +1658,7 @@
   final bool required;
 
   _SimpleKey(this.tokenNumber, this.line, this.column, this.location,
-      {bool required})
+      {required bool required})
       : required = required;
 }
 
diff --git a/pkgs/yaml/lib/src/token.dart b/pkgs/yaml/lib/src/token.dart
index 8416554..711950c 100644
--- a/pkgs/yaml/lib/src/token.dart
+++ b/pkgs/yaml/lib/src/token.dart
@@ -93,9 +93,9 @@
   final FileSpan span;
 
   /// The tag handle for named tags.
-  final String handle;
+  final String? handle;
 
-  /// The tag suffix, or `null`.
+  /// The tag suffix.
   final String suffix;
 
   TagToken(this.span, this.handle, this.suffix);
diff --git a/pkgs/yaml/lib/src/utils.dart b/pkgs/yaml/lib/src/utils.dart
index 8ce7855..2c69ead 100644
--- a/pkgs/yaml/lib/src/utils.dart
+++ b/pkgs/yaml/lib/src/utils.dart
@@ -18,7 +18,7 @@
 /// Print a warning.
 ///
 /// If [span] is passed, associates the warning with that span.
-void warn(String message, [SourceSpan span]) =>
+void warn(String message, [SourceSpan? span]) =>
     yamlWarningCallback(message, span);
 
 /// A callback for emitting a warning.
@@ -26,14 +26,14 @@
 /// [message] is the text of the warning. If [span] is passed, it's the portion
 /// of the document that the warning is associated with and should be included
 /// in the printed warning.
-typedef YamlWarningCallback = Function(String message, [SourceSpan span]);
+typedef YamlWarningCallback = Function(String message, [SourceSpan? span]);
 
 /// A callback for emitting a warning.
 ///
 /// In a very few cases, the YAML spec indicates that an implementation should
 /// emit a warning. To do so, it calls this callback. The default implementation
 /// prints a message using [print].
-YamlWarningCallback yamlWarningCallback = (message, [span]) {
+YamlWarningCallback yamlWarningCallback = (message, [SourceSpan? span]) {
   // TODO(nweiz): Print to stderr with color when issue 6943 is fixed and
   // dart:io is available.
   if (span != null) message = span.message(message);
diff --git a/pkgs/yaml/lib/src/yaml_document.dart b/pkgs/yaml/lib/src/yaml_document.dart
index 8757418..65b9548 100644
--- a/pkgs/yaml/lib/src/yaml_document.dart
+++ b/pkgs/yaml/lib/src/yaml_document.dart
@@ -17,7 +17,7 @@
   final SourceSpan span;
 
   /// The version directive for the document, if any.
-  final VersionDirective versionDirective;
+  final VersionDirective? versionDirective;
 
   /// The tag directives for the document.
   final List<TagDirective> tagDirectives;
diff --git a/pkgs/yaml/lib/src/yaml_exception.dart b/pkgs/yaml/lib/src/yaml_exception.dart
index c4b7f28..1941f02 100644
--- a/pkgs/yaml/lib/src/yaml_exception.dart
+++ b/pkgs/yaml/lib/src/yaml_exception.dart
@@ -6,5 +6,5 @@
 
 /// An error thrown by the YAML processor.
 class YamlException extends SourceSpanFormatException {
-  YamlException(String message, SourceSpan span) : super(message, span);
+  YamlException(String message, SourceSpan? span) : super(message, span);
 }
diff --git a/pkgs/yaml/lib/src/yaml_node.dart b/pkgs/yaml/lib/src/yaml_node.dart
index bbba77e..3e3ae8d 100644
--- a/pkgs/yaml/lib/src/yaml_node.dart
+++ b/pkgs/yaml/lib/src/yaml_node.dart
@@ -27,9 +27,10 @@
   /// [SourceSpan.message] can be used to produce a human-friendly message about
   /// this node.
   SourceSpan get span => _span;
-
   SourceSpan _span;
 
+  YamlNode._(this._span);
+
   /// The inner value of this node.
   ///
   /// For [YamlScalar]s, this will return the wrapped value. For [YamlMap] and
@@ -83,9 +84,8 @@
 
   /// Users of the library should not use this constructor.
   YamlMap.internal(Map<dynamic, YamlNode> nodes, SourceSpan span, this.style)
-      : nodes = UnmodifiableMapView<dynamic, YamlNode>(nodes) {
-    _span = span;
-  }
+      : nodes = UnmodifiableMapView<dynamic, YamlNode>(nodes),
+        super._(span);
 
   @override
   dynamic operator [](key) => nodes[key]?.value;
@@ -134,9 +134,8 @@
 
   /// Users of the library should not use this constructor.
   YamlList.internal(List<YamlNode> nodes, SourceSpan span, this.style)
-      : nodes = UnmodifiableListView<YamlNode>(nodes) {
-    _span = span;
-  }
+      : nodes = UnmodifiableListView<YamlNode>(nodes),
+        super._(span);
 
   @override
   dynamic operator [](int index) => nodes[index].value;
@@ -162,21 +161,20 @@
   /// [sourceUrl] is passed, it's used as the [SourceSpan.sourceUrl].
   ///
   /// [sourceUrl] may be either a [String], a [Uri], or `null`.
-  YamlScalar.wrap(this.value, {sourceUrl, this.style = ScalarStyle.ANY}) {
+  YamlScalar.wrap(this.value, {sourceUrl, this.style = ScalarStyle.ANY})
+      : super._(NullSpan(sourceUrl)) {
     ArgumentError.checkNotNull(style, 'style');
-    _span = NullSpan(sourceUrl);
   }
 
   /// Users of the library should not use this constructor.
-  YamlScalar.internal(this.value, ScalarEvent scalar) : style = scalar.style {
-    _span = scalar.span;
-  }
+  YamlScalar.internal(this.value, ScalarEvent scalar)
+      : style = scalar.style,
+        super._(scalar.span);
 
   /// Users of the library should not use this constructor.
   YamlScalar.internalWithSpan(this.value, SourceSpan span)
-      : style = ScalarStyle.ANY {
-    _span = span;
-  }
+      : style = ScalarStyle.ANY,
+        super._(span);
 
   @override
   String toString() => value.toString();
diff --git a/pkgs/yaml/lib/src/yaml_node_wrapper.dart b/pkgs/yaml/lib/src/yaml_node_wrapper.dart
index 1e44a09..87328ba 100644
--- a/pkgs/yaml/lib/src/yaml_node_wrapper.dart
+++ b/pkgs/yaml/lib/src/yaml_node_wrapper.dart
@@ -45,7 +45,7 @@
   }
 
   @override
-  dynamic operator [](Object key) {
+  dynamic operator [](Object? key) {
     var value = _dartMap[key];
     if (value is Map) return YamlMapWrapper._(value, span);
     if (value is List) return YamlListWrapper._(value, span);
@@ -75,9 +75,9 @@
   _YamlMapNodes(this._dartMap, this._span);
 
   @override
-  YamlNode operator [](Object key) {
+  YamlNode? operator [](Object? key) {
     // Use "as" here because key being assigned to invalidates type propagation.
-    if (key is YamlScalar) key = (key as YamlScalar).value;
+    if (key is YamlScalar) key = key.value;
     if (!_dartMap.containsKey(key)) return null;
     return _nodeForValue(_dartMap[key], _span);
   }
diff --git a/pkgs/yaml/pubspec.yaml b/pkgs/yaml/pubspec.yaml
index 31e5001..10d1004 100644
--- a/pkgs/yaml/pubspec.yaml
+++ b/pkgs/yaml/pubspec.yaml
@@ -1,19 +1,25 @@
 name: yaml
-version: 2.3.0-dev
+version: 3.0.0-nullsafety.0
 
 description: A parser for YAML, a human-friendly data serialization standard
 homepage: https://github.com/dart-lang/yaml
 
 environment:
-  sdk: ">=2.4.0 <3.0.0"
+  sdk: '>=2.12.0-0 <3.0.0'
 
 dependencies:
-  charcode: ^1.1.0
-  collection: ">=1.1.0 <2.0.0"
-  string_scanner: ">=0.1.4 <2.0.0"
-  source_span: ">=1.0.0 <2.0.0"
+  charcode: ^1.2.0-nullsafety
+  collection: ^1.15.0-nullsafety
+  source_span: ^1.8.0-nullsafety
+  string_scanner: ^1.1.0-nullsafety
 
 dev_dependencies:
-  pedantic: ^1.0.0
-  path: ">=1.2.0 <2.0.0"
-  test: ">=0.12.0 <2.0.0"
+  path: ^1.8.0-nullsafety
+  pedantic: ^1.10.0-nullsafety
+  test: ^1.16.0-nullsafety
+
+dependency_overrides:
+  analyzer: ^0.40.0
+  test: ^1.16.0-nullsafety
+  test_api: ^0.2.19-nullsafety
+  test_core: ^0.3.12-nullsafety
diff --git a/pkgs/yaml/test/span_test.dart b/pkgs/yaml/test/span_test.dart
index 4017868..b8170e6 100644
--- a/pkgs/yaml/test/span_test.dart
+++ b/pkgs/yaml/test/span_test.dart
@@ -16,7 +16,7 @@
 }
 
 void main() {
-  YamlMap yaml;
+  late YamlMap yaml;
 
   setUpAll(() {
     yaml = loadYaml(const JsonEncoder.withIndent(' ').convert({
@@ -31,7 +31,7 @@
 
   test('first root key', () {
     _expectSpan(
-      yaml.nodes['num'].span,
+      yaml.nodes['num']!.span,
       r'''
 line 2, column 9: message

@@ -43,7 +43,7 @@
 
   test('first root key', () {
     _expectSpan(
-      yaml.nodes['null'].span,
+      yaml.nodes['null']!.span,
       r'''
 line 7, column 10: message

@@ -54,7 +54,7 @@
   });
 
   group('nested', () {
-    YamlMap nestedMap;
+    late YamlMap nestedMap;
 
     setUpAll(() {
       nestedMap = yaml.nodes['nested'] as YamlMap;
@@ -62,7 +62,7 @@
 
     test('first root key', () {
       _expectSpan(
-        nestedMap.nodes['null'].span,
+        nestedMap.nodes['null']!.span,
         r'''
 line 4, column 11: message

@@ -74,7 +74,7 @@
 
     test('first root key', () {
       _expectSpan(
-        nestedMap.nodes['num'].span,
+        nestedMap.nodes['num']!.span,
         r'''
 line 5, column 10: message

@@ -88,7 +88,7 @@
   });
 
   group('block', () {
-    YamlList list, nestedList;
+    late YamlList list, nestedList;
 
     setUpAll(() {
       const yamlStr = '''
diff --git a/pkgs/yaml/test/utils.dart b/pkgs/yaml/test/utils.dart
index c49afe2..ce05481 100644
--- a/pkgs/yaml/test/utils.dart
+++ b/pkgs/yaml/test/utils.dart
@@ -16,7 +16,7 @@
     (actual) => equality.deepEquals(actual, expected), 'equals $expected');
 
 /// Constructs a new yaml.YamlMap, optionally from a normal Map.
-Map deepEqualsMap([Map from]) {
+Map deepEqualsMap([Map? from]) {
   var map = equality.deepEqualsMap();
   if (from != null) map.addAll(from);
   return map;
diff --git a/pkgs/yaml/test/yaml_node_wrapper_test.dart b/pkgs/yaml/test/yaml_node_wrapper_test.dart
index d6da052..e5d71cc 100644
--- a/pkgs/yaml/test/yaml_node_wrapper_test.dart
+++ b/pkgs/yaml/test/yaml_node_wrapper_test.dart
@@ -61,8 +61,8 @@
     expect(map['map']['nested'], TypeMatcher<YamlList>());
     expect(map['map'].span, isNullSpan(isNull));
     expect(map.nodes['scalar'], TypeMatcher<YamlScalar>());
-    expect(map.nodes['scalar'].value, 'value');
-    expect(map.nodes['scalar'].span, isNullSpan(isNull));
+    expect(map.nodes['scalar']!.value, 'value');
+    expect(map.nodes['scalar']!.span, isNullSpan(isNull));
     expect(map['scalar'], 'value');
     expect(map.keys, unorderedEquals(['list', 'map', 'scalar']));
     expect(map.nodes.keys, everyElement(TypeMatcher<YamlScalar>()));
@@ -88,7 +88,7 @@
     expect(map.span, isNullSpan(source));
     expect(map['list'].span, isNullSpan(source));
     expect(map['map'].span, isNullSpan(source));
-    expect(map.nodes['scalar'].span, isNullSpan(source));
+    expect(map.nodes['scalar']!.span, isNullSpan(source));
   });
 
   test('YamlMap.wrap() with a sourceUrl and style', () {
@@ -217,7 +217,7 @@
   });
 }
 
-Matcher isNullSpan(sourceUrl) => predicate((span) {
+Matcher isNullSpan(sourceUrl) => predicate((SourceSpan span) {
       expect(span, TypeMatcher<SourceSpan>());
       expect(span.length, equals(0));
       expect(span.text, isEmpty);