Merge null_safety branch into master (#24)

diff --git a/.travis.yml b/.travis.yml
index d2a0ee3..5647d2c 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,24 +1,38 @@
 language: dart
 
 dart:
-  - 2.4.0
-  - dev
+- dev
 
-dart_task:
-  - test: -p vm
-    xvfb: false
-  # Set concurrency to 1 to avoid flakes on Travis
-  - test: -p firefox -j 1
-  - dartanalyzer: --fatal-infos --fatal-warnings .
-
-matrix:
+jobs:
   include:
-  - dart: dev
-    dart_task: dartfmt
+    - stage: analyze_and_format
+      name: "Analyze"
+      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 
+    - stage: test
+      name: "Web Tests"
+      dart: dev
+      os: linux
+      script: pub run --enable-experiment=non-nullable test -p chrome
+
+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/CHANGELOG.md b/CHANGELOG.md
index 7e2aef0..63beaaf 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 2.1.0-nullsafety
+
+* Migrate to null safety. There are no expected semantic changes.
+
 ## 2.0.0
 
 * Breaking: `BooleanSelector.evaluate` always takes a `bool Function(String)`.
diff --git a/analysis_options.yaml b/analysis_options.yaml
index 73acb8d..e4e899c 100644
--- a/analysis_options.yaml
+++ b/analysis_options.yaml
@@ -1,4 +1,7 @@
 include: package:pedantic/analysis_options.yaml
+analyzer:
+  enable-experiment:
+    - non-nullable
 linter:
   rules:
     - avoid_null_checks_in_equality_operators
diff --git a/lib/src/all.dart b/lib/src/all.dart
index 9255709..3424319 100644
--- a/lib/src/all.dart
+++ b/lib/src/all.dart
@@ -9,7 +9,7 @@
   // TODO(nweiz): Stop explicitly providing a type argument when sdk#32412 is
   // fixed.
   @override
-  final variables = const <String>[];
+  final Iterable<String> variables = const <String>[];
 
   const All();
 
diff --git a/lib/src/ast.dart b/lib/src/ast.dart
index d16098a..0adfe99 100644
--- a/lib/src/ast.dart
+++ b/lib/src/ast.dart
@@ -15,7 +15,7 @@
   /// statically-parsed annotation or from a parameter.
   ///
   /// This may be `null` for nodes without source information.
-  FileSpan get span;
+  FileSpan? get span;
 
   /// All the variables in this node, in the order they appear.
   Iterable<String> get variables;
@@ -27,7 +27,7 @@
 /// A single variable.
 class VariableNode implements Node {
   @override
-  final FileSpan span;
+  final FileSpan? span;
 
   /// The variable name.
   final String name;
@@ -53,7 +53,7 @@
 /// A negation expression.
 class NotNode implements Node {
   @override
-  final FileSpan span;
+  final FileSpan? span;
 
   /// The expression being negated.
   final Node child;
@@ -80,7 +80,7 @@
 /// An or expression.
 class OrNode implements Node {
   @override
-  FileSpan get span => _expandSafe(left.span, right.span);
+  FileSpan? get span => _expandSafe(left.span, right.span);
 
   /// The left-hand branch of the expression.
   final Node left;
@@ -119,7 +119,7 @@
 /// An and expression.
 class AndNode implements Node {
   @override
-  FileSpan get span => _expandSafe(left.span, right.span);
+  FileSpan? get span => _expandSafe(left.span, right.span);
 
   /// The left-hand branch of the expression.
   final Node left;
@@ -158,7 +158,7 @@
 /// A ternary conditional expression.
 class ConditionalNode implements Node {
   @override
-  FileSpan get span => _expandSafe(condition.span, whenFalse.span);
+  FileSpan? get span => _expandSafe(condition.span, whenFalse.span);
 
   /// The condition expression to check.
   final Node condition;
@@ -203,7 +203,7 @@
 
 /// Like [FileSpan.expand], except if [start] and [end] are `null` or from
 /// different files it returns `null` rather than throwing an error.
-FileSpan _expandSafe(FileSpan start, FileSpan end) {
+FileSpan? _expandSafe(FileSpan? start, FileSpan? end) {
   if (start == null || end == null) return null;
   if (start.file != end.file) return null;
   return start.expand(end);
diff --git a/lib/src/none.dart b/lib/src/none.dart
index 42340b1..08ac08a 100644
--- a/lib/src/none.dart
+++ b/lib/src/none.dart
@@ -6,10 +6,8 @@
 
 /// A selector that matches no inputs.
 class None implements BooleanSelector {
-  // TODO(nweiz): Stop explicitly providing a type argument when sdk#32412 is
-  // fixed.
   @override
-  final variables = const <String>[];
+  final Iterable<String> variables = const [];
 
   const None();
 
diff --git a/lib/src/parser.dart b/lib/src/parser.dart
index 1a0a73b..368ce5e 100644
--- a/lib/src/parser.dart
+++ b/lib/src/parser.dart
@@ -83,7 +83,7 @@
     switch (token.type) {
       case TokenType.not:
         var child = _simpleExpression();
-        return NotNode(child, token.span.expand(child.span));
+        return NotNode(child, token.span.expand(child.span!));
 
       case TokenType.leftParen:
         var child = _conditional();
diff --git a/lib/src/scanner.dart b/lib/src/scanner.dart
index 3516817..00e5ef7 100644
--- a/lib/src/scanner.dart
+++ b/lib/src/scanner.dart
@@ -29,7 +29,7 @@
   final SpanScanner _scanner;
 
   /// The next token to emit.
-  Token _next;
+  Token? _next;
 
   /// Whether the scanner has emitted a [TokenType.endOfFile] token.
   bool _endOfFileEmitted = false;
@@ -124,7 +124,7 @@
   /// Scans and returns an identifier token.
   Token _scanIdentifier() {
     _scanner.expect(_hyphenatedIdentifier, name: 'expression');
-    return IdentifierToken(_scanner.lastMatch[0], _scanner.lastSpan);
+    return IdentifierToken(_scanner.lastMatch![0]!, _scanner.lastSpan!);
   }
 
   /// Consumes all whitespace and comments immediately following the cursor's
diff --git a/pubspec.yaml b/pubspec.yaml
index 73cd68b..15616a4 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,22 +1,94 @@
 name: boolean_selector
-version: 2.0.0
+version: 2.1.0-nullsafety
 description: >-
   A flexible syntax for boolean expressions, based on a simplified version of
   Dart's expression syntax.
 homepage: https://github.com/dart-lang/boolean_selector
 
 environment:
-  sdk: '>=2.4.0 <3.0.0'
+  sdk: '>=2.9.0-18.0 <2.9.0'
 
 dependencies:
-  source_span: ^1.0.0
-  string_scanner: ^1.0.0
+  source_span: '>=1.8.0-nullsafety <1.8.0'
+  string_scanner: '>=1.1.0-nullsafety <1.1.0'
 
 dev_dependencies:
   pedantic: ^1.0.0
-  test: ^1.2.0
+  test: any
+  test_api: any
+  test_core: any
 
 dependency_overrides:
-  test: 1.11.1
-  test_api: 0.2.13
-  test_core: 0.2.18
+  async:
+    git:
+      url: git://github.com/dart-lang/async.git
+      ref: null_safety
+  charcode:
+    git:
+      url: git://github.com/dart-lang/charcode.git
+      ref: null_safety
+  collection: 1.15.0-nullsafety
+  js:
+    git:
+      url: git://github.com/dart-lang/sdk.git
+      path: pkg/js
+  matcher:
+    git:
+      url: git://github.com/dart-lang/matcher.git
+      ref: null_safety
+  meta: 1.3.0-nullsafety
+  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_maps:
+    git:
+      url: git://github.com/dart-lang/source_maps.git
+      ref: null_safety
+  source_map_stack_trace:
+    git:
+      url: git://github.com/dart-lang/source_map_stack_trace.git
+      ref: null_safety
+  source_span:
+    git:
+      url: git://github.com/dart-lang/source_span.git
+      ref: null_safety
+  stack_trace:
+    git:
+      url: git://github.com/dart-lang/stack_trace.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/evaluate_test.dart b/test/evaluate_test.dart
index 5dfc2cc..eb69335 100644
--- a/test/evaluate_test.dart
+++ b/test/evaluate_test.dart
@@ -46,7 +46,7 @@
 ///
 /// By default, "true" is true and all other variables are "false".
 void _expectEval(String expression, bool result,
-    {bool Function(String variable) semantics}) {
+    {bool Function(String variable)? semantics}) {
   expect(_eval(expression, semantics: semantics), equals(result),
       reason: 'Expected "$expression" to evaluate to $result.');
 }
@@ -54,7 +54,7 @@
 /// Returns the result of evaluating [expression] on [semantics].
 ///
 /// By default, "true" is true and all other variables are "false".
-bool _eval(String expression, {bool Function(String variable) semantics}) {
+bool _eval(String expression, {bool Function(String variable)? semantics}) {
   var selector = BooleanSelector.parse(expression);
   return selector.evaluate(semantics ?? (v) => v == 'true');
 }
diff --git a/test/parser_test.dart b/test/parser_test.dart
index 5a1bd83..5eca512 100644
--- a/test/parser_test.dart
+++ b/test/parser_test.dart
@@ -254,10 +254,10 @@
 /// A matcher that asserts that a value is a [VariableNode] with the given
 /// [name].
 Matcher _isVar(String name) => predicate(
-    (value) => value is VariableNode && value.name == name,
+    (dynamic value) => value is VariableNode && value.name == name,
     'is a variable named "$name"');
 
-void _expectToString(String selector, [String result]) {
+void _expectToString(String selector, [String? result]) {
   result ??= selector;
   expect(_toString(selector), equals(result),
       reason: 'Expected toString of "$selector" to be "$result".');
diff --git a/test/to_string_test.dart b/test/to_string_test.dart
index ec1b4de..1047fd1 100644
--- a/test/to_string_test.dart
+++ b/test/to_string_test.dart
@@ -76,7 +76,7 @@
   });
 }
 
-void _expectToString(String selector, [String result]) {
+void _expectToString(String selector, [String? result]) {
   result ??= selector;
   expect(_toString(selector), equals(result),
       reason: 'Expected toString of "$selector" to be "$result".');