Enable null safety (#33)
diff --git a/.travis.yml b/.travis.yml index 59d861d..cc94606 100644 --- a/.travis.yml +++ b/.travis.yml
@@ -1,24 +1,34 @@ language: dart dart: - - 2.7.0 - dev -dart_task: - - test: --platform vm - xvfb: false - - test: --platform firefox - - dartanalyzer: --fatal-infos --fatal-warnings . - -matrix: +jobs: include: - # Only validate formatting using the dev release - - dart: dev - dart_task: dartfmt + - stage: analyze_and_format + name: "Analyze" + os: linux + script: dartanalyzer --enable-experiment=non-nullable --fatal-warnings --fatal-infos . + - stage: analyze_and_format + name: "Format" + os: linux + script: dartfmt -n --set-exit-if-changed . + - stage: test + name: "Vm Tests" + os: linux + script: pub run --enable-experiment=non-nullable test -p vm + - stage: test + name: "Web Tests" + 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 b9ad843..1bd1ae5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md
@@ -1,6 +1,13 @@ -## 3.1.5-dev +## 3.2.0-nullsafety -* Require minimum Dart SDK of `2.7.0`. +Pre-release for the null safety migration of this package. + +Note that 3.2.0 may not be the final stable null safety release version, we +reserve the right to release it as a 4.0.0 breaking change. + +This release will be pinned to only allow pre-release sdk versions starting from +2.10.0-2.0.dev, which is the first version where this package will appear in the +null safety allow list. ## 3.1.4
diff --git a/analysis_options.yaml b/analysis_options.yaml index 69009d3..f98644b 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml
@@ -3,6 +3,8 @@ analyzer: strong-mode: implicit-casts: false + enable-experiment: + - non-nullable linter: rules:
diff --git a/lib/src/authentication_challenge.dart b/lib/src/authentication_challenge.dart index 9b554e8..356e0a2 100644 --- a/lib/src/authentication_challenge.dart +++ b/lib/src/authentication_challenge.dart
@@ -59,7 +59,7 @@ if (scanner.matches(',') || scanner.isDone) continue; scanner.expect(token, name: 'a token'); - final name = scanner.lastMatch[0]; + final name = scanner.lastMatch![0]!; scanner.scan(whitespace); // If there's no "=", then this is another challenge rather than a @@ -72,7 +72,7 @@ scanner.scan(whitespace); if (scanner.scan(token)) { - params[name] = scanner.lastMatch[0]; + params[name] = scanner.lastMatch![0]!; } else { params[name] = expectQuotedString(scanner, name: 'a token or a quoted string'); @@ -109,15 +109,15 @@ /// /// If [whitespaceName] is passed, it's used as the name for exceptions thrown /// due to invalid trailing whitespace. - static String _scanScheme(StringScanner scanner, {String whitespaceName}) { + static String _scanScheme(StringScanner scanner, {String? whitespaceName}) { scanner.expect(token, name: 'a token'); - final scheme = scanner.lastMatch[0].toLowerCase(); + final scheme = scanner.lastMatch![0]!.toLowerCase(); scanner.scan(whitespace); // The spec specifically requires a space between the scheme and its // params. - if (scanner.lastMatch == null || !scanner.lastMatch[0].contains(' ')) { + if (scanner.lastMatch == null || !scanner.lastMatch![0]!.contains(' ')) { scanner.expect(' ', name: whitespaceName); } @@ -127,13 +127,13 @@ /// Scans a single authentication parameter and stores its result in [params]. static void _scanAuthParam(StringScanner scanner, Map params) { scanner.expect(token, name: 'a token'); - final name = scanner.lastMatch[0]; + final name = scanner.lastMatch![0]; scanner.scan(whitespace); scanner.expect('='); scanner.scan(whitespace); if (scanner.scan(token)) { - params[name] = scanner.lastMatch[0]; + params[name] = scanner.lastMatch![0]; } else { params[name] = expectQuotedString(scanner, name: 'a token or a quoted string');
diff --git a/lib/src/case_insensitive_map.dart b/lib/src/case_insensitive_map.dart index 870f84c..88a190e 100644 --- a/lib/src/case_insensitive_map.dart +++ b/lib/src/case_insensitive_map.dart
@@ -8,10 +8,8 @@ /// /// Much of HTTP is case-insensitive, so this is useful to have pre-defined. class CaseInsensitiveMap<V> extends CanonicalizedMap<String, String, V> { - CaseInsensitiveMap() - : super((key) => key.toLowerCase(), isValidKey: (key) => key != null); + CaseInsensitiveMap() : super((key) => key.toLowerCase()); CaseInsensitiveMap.from(Map<String, V> other) - : super.from(other, (key) => key.toLowerCase(), - isValidKey: (key) => key != null); + : super.from(other, (key) => key.toLowerCase()); }
diff --git a/lib/src/chunked_coding/decoder.dart b/lib/src/chunked_coding/decoder.dart index dcc0188..a916870 100644 --- a/lib/src/chunked_coding/decoder.dart +++ b/lib/src/chunked_coding/decoder.dart
@@ -2,6 +2,7 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. +import 'dart:async'; import 'dart:convert'; import 'dart:math' as math; import 'dart:typed_data'; @@ -18,7 +19,7 @@ @override List<int> convert(List<int> input) { - final sink = _Sink(null); + final sink = _Sink(StreamController()); final output = sink._decode(input, 0, input.length); if (sink._state == _State.end) return output; @@ -38,9 +39,10 @@ /// The current state of the sink's parsing. var _state = _State.boundary; - /// The size of the chunk being parsed, or `null` if the size hasn't been - /// parsed yet. - int _size; + /// The size of the chunk being parsed. + /// + /// Only assigned and used within [_decode]. + late int _size; _Sink(this._sink); @@ -60,7 +62,7 @@ /// Like [close], but includes [chunk] and [index] in the [FormatException] if /// one is thrown. - void _close([List<int> chunk, int index]) { + void _close([List<int>? chunk, int? index]) { if (_state != _State.end) { throw FormatException('Input ended unexpectedly.', chunk, index); }
diff --git a/lib/src/http_date.dart b/lib/src/http_date.dart index 71f6bab..0cedd9a 100644 --- a/lib/src/http_date.dart +++ b/lib/src/http_date.dart
@@ -113,17 +113,17 @@ int _parseMonth(StringScanner scanner) { scanner.expect(_monthRegExp); // DateTime uses 1-indexed months. - return _months.indexOf(scanner.lastMatch[0]) + 1; + return _months.indexOf(scanner.lastMatch![0]!) + 1; } /// Parses an int an enforces that it has exactly [digits] digits. int _parseInt(StringScanner scanner, int digits) { scanner.expect(_digitRegExp); - if (scanner.lastMatch[0].length != digits) { + if (scanner.lastMatch![0]!.length != digits) { scanner.error('expected a $digits-digit number.'); } - return int.parse(scanner.lastMatch[0]); + return int.parse(scanner.lastMatch![0]!); } /// Parses an timestamp of the form "HH:MM:SS" on a 24-hour clock.
diff --git a/lib/src/media_type.dart b/lib/src/media_type.dart index 71b16b2..59e6928 100644 --- a/lib/src/media_type.dart +++ b/lib/src/media_type.dart
@@ -47,22 +47,22 @@ final scanner = StringScanner(mediaType); scanner.scan(whitespace); scanner.expect(token); - final type = scanner.lastMatch[0]; + final type = scanner.lastMatch![0]!; scanner.expect('/'); scanner.expect(token); - final subtype = scanner.lastMatch[0]; + final subtype = scanner.lastMatch![0]!; scanner.scan(whitespace); final parameters = <String, String>{}; while (scanner.scan(';')) { scanner.scan(whitespace); scanner.expect(token); - final attribute = scanner.lastMatch[0]; + final attribute = scanner.lastMatch![0]!; scanner.expect('='); String value; if (scanner.scan(token)) { - value = scanner.lastMatch[0]; + value = scanner.lastMatch![0]!; } else { value = expectQuotedString(scanner); } @@ -75,7 +75,7 @@ return MediaType(type, subtype, parameters); }); - MediaType(String type, String subtype, [Map<String, String> parameters]) + MediaType(String type, String subtype, [Map<String, String>? parameters]) : type = type.toLowerCase(), subtype = subtype.toLowerCase(), parameters = UnmodifiableMapView( @@ -91,10 +91,10 @@ /// [clearParameters] is passed, it replaces the corresponding field entirely /// instead. MediaType change( - {String type, - String subtype, - String mimeType, - Map<String, String> parameters, + {String? type, + String? subtype, + String? mimeType, + Map<String, String>? parameters, bool clearParameters = false}) { if (mimeType != null) { if (type != null) {
diff --git a/lib/src/scan.dart b/lib/src/scan.dart index 1bdf877..d7de54d 100644 --- a/lib/src/scan.dart +++ b/lib/src/scan.dart
@@ -58,11 +58,11 @@ /// /// If [name] is passed, it's used to describe the expected value if it's not /// found. -String expectQuotedString(StringScanner scanner, {String name}) { +String expectQuotedString(StringScanner scanner, {String? name}) { name ??= 'quoted string'; scanner.expect(_quotedString, name: name); - final string = scanner.lastMatch[0]; + final string = scanner.lastMatch![0]!; return string .substring(1, string.length - 1) - .replaceAllMapped(_quotedPair, (match) => match[1]); + .replaceAllMapped(_quotedPair, (match) => match[1]!); }
diff --git a/pubspec.yaml b/pubspec.yaml index eccff2c..d292ea1 100644 --- a/pubspec.yaml +++ b/pubspec.yaml
@@ -1,19 +1,19 @@ name: http_parser -version: 3.1.5-dev +version: 3.2.0-nullsafety description: >- A platform-independent package for parsing and serializing HTTP formats. homepage: https://github.com/dart-lang/http_parser environment: - sdk: '>=2.7.0 <3.0.0' + sdk: '>=2.10.0-0 <2.10.0' dependencies: - charcode: ^1.1.0 - collection: '>=0.9.1 <2.0.0' - source_span: ^1.0.0 - string_scanner: '>=0.0.0 <2.0.0' - typed_data: ^1.1.0 + charcode: ^1.2.0-nullsafety + collection: ^1.15.0-nullsafety.2 + source_span: ^1.8.0-nullsafety + string_scanner: ^1.1.0-nullsafety + typed_data: ^1.3.0-nullsafety.2 dev_dependencies: - pedantic: ^1.0.0 - test: ^1.0.0 + pedantic: ^1.10.0-nullsafety + test: ^1.16.0-nullsafety.4
diff --git a/test/chunked_coding_test.dart b/test/chunked_coding_test.dart index 0b178ec..045557a 100644 --- a/test/chunked_coding_test.dart +++ b/test/chunked_coding_test.dart
@@ -30,8 +30,8 @@ }); group('with chunked conversion', () { - List<List<int>> results; - ByteConversionSink sink; + late List<List<int>> results; + late ByteConversionSink sink; setUp(() { results = []; final controller = StreamController<List<int>>(sync: true); @@ -255,8 +255,8 @@ }); group('with chunked conversion', () { - List<List<int>> results; - ByteConversionSink sink; + late List<List<int>> results; + late ByteConversionSink sink; setUp(() { results = []; final controller = StreamController<List<int>>(sync: true);
diff --git a/test/example_test.dart b/test/example_test.dart index ef61060..bbf869f 100644 --- a/test/example_test.dart +++ b/test/example_test.dart
@@ -6,7 +6,10 @@ test('validate example', () { final result = Process.runSync( Platform.executable, - ['example/example.dart'], + [ + '--enable-experiment=non-nullable', + 'example/example.dart', + ], ); expect(result.exitCode, 0);
diff --git a/test/media_type_test.dart b/test/media_type_test.dart index ceca7b9..5ee9313 100644 --- a/test/media_type_test.dart +++ b/test/media_type_test.dart
@@ -83,7 +83,7 @@ }); group('change', () { - MediaType type; + late MediaType type; setUp(() { type = MediaType.parse('text/plain; foo=bar; baz=bang'); });