Default language version (#2748)
* Added a default language version of 2.7
* Fixed tests
diff --git a/lib/src/entrypoint.dart b/lib/src/entrypoint.dart
index 574c25e..b9f9eed 100644
--- a/lib/src/entrypoint.dart
+++ b/lib/src/entrypoint.dart
@@ -18,6 +18,7 @@
import 'executable.dart';
import 'http.dart' as http;
import 'io.dart';
+import 'language_version.dart';
import 'lock_file.dart';
import 'log.dart' as log;
import 'package.dart';
@@ -753,7 +754,7 @@
try {
// Load `pubspec.yaml` and extract language version to compare with the
// language version from `package_config.json`.
- final languageVersion = extractLanguageVersion(
+ final languageVersion = LanguageVersion.fromSdkConstraint(
cache.load(id).pubspec.sdkConstraints[sdk.identifier],
);
if (pkg.languageVersion != languageVersion) {
diff --git a/lib/src/language_version.dart b/lib/src/language_version.dart
index 4018e3c..ff7a67c 100644
--- a/lib/src/language_version.dart
+++ b/lib/src/language_version.dart
@@ -5,6 +5,8 @@
import 'package:analyzer/dart/ast/token.dart';
import 'package:pub_semver/pub_semver.dart';
+final _languageVersionPattern = RegExp(r'^(\d+)\.(\d+)$');
+
/// A Dart language version as defined by
/// https://github.com/dart-lang/language/blob/master/accepted/future-releases/language-versioning/feature-specification.md
class LanguageVersion implements Comparable<LanguageVersion> {
@@ -14,20 +16,52 @@
const LanguageVersion(this.major, this.minor);
/// The language version implied by a Dart sdk version.
- factory LanguageVersion.fromVersion(Version version) =>
- LanguageVersion(version.major, version.minor);
+ factory LanguageVersion.fromVersion(Version version) {
+ ArgumentError.checkNotNull(version, 'version');
+ return LanguageVersion(version.major, version.minor);
+ }
- /// The language version implied by a Dart sdk version range.
- ///
- /// Throws if the versionRange has no lower bound.
- factory LanguageVersion.fromVersionRange(VersionRange range) {
- final min = range.min;
- if (min == null) {
- // TODO(sigurdm): is this right?
- throw ArgumentError(
- 'Version range with no lower bound does not imply a language version');
+ /// Parse language version from string.
+ factory LanguageVersion.parse(String languageVersion) {
+ ArgumentError.checkNotNull(languageVersion, 'languageVersion');
+ final m = _languageVersionPattern.firstMatch(languageVersion);
+ if (m == null) {
+ throw FormatException(
+ 'Invalid language version string',
+ languageVersion,
+ );
}
- return LanguageVersion(min.major, min.minor);
+ return LanguageVersion(
+ int.parse(m.group(1)),
+ int.parse(m.group(2)),
+ );
+ }
+
+ /// The language version implied by a Dart SDK constraint in `pubspec.yaml`.
+ /// (this is `environment: {sdk: '>=2.0.0 <3.0.0'}` from `pubspec.yaml`)
+ ///
+ /// Fallbacks to [defaultLanguageVersion] if there is no [sdkConstraint] or
+ /// the [sdkConstraint] has no lower-bound.
+ factory LanguageVersion.fromSdkConstraint(VersionConstraint sdkConstraint) {
+ if (sdkConstraint == null || sdkConstraint.isEmpty) {
+ return defaultLanguageVersion;
+ } else if (sdkConstraint is Version) {
+ return LanguageVersion.fromVersion(sdkConstraint);
+ } else if (sdkConstraint is VersionRange) {
+ if (sdkConstraint.min != null) {
+ return LanguageVersion.fromVersion(sdkConstraint.min);
+ }
+ return defaultLanguageVersion;
+ } else if (sdkConstraint is VersionUnion) {
+ // `ranges` is non-empty and sorted.
+ final min = sdkConstraint.ranges.first.min;
+ if (min != null) {
+ return LanguageVersion.fromVersion(min);
+ }
+ return defaultLanguageVersion;
+ } else {
+ throw ArgumentError('Unknown VersionConstraint type $sdkConstraint.');
+ }
}
/// The language version implied by a Dart sdk version.
@@ -48,8 +82,18 @@
bool operator <=(LanguageVersion other) => compareTo(other) <= 0;
bool operator >=(LanguageVersion other) => compareTo(other) >= 0;
+ @override
+ int get hashCode => major ^ minor;
+
+ @override
+ bool operator ==(Object other) =>
+ other is LanguageVersion && other.minor == minor && other.major == major;
+
+ static const defaultLanguageVersion = LanguageVersion(2, 7);
static const firstVersionWithNullSafety = LanguageVersion(2, 12);
+ /// Transform language version to string that can be parsed with
+ /// [LanguageVersion.parse].
@override
String toString() => '$major.$minor';
}
diff --git a/lib/src/lock_file.dart b/lib/src/lock_file.dart
index da677d0..23cf4e9 100644
--- a/lib/src/lock_file.dart
+++ b/lib/src/lock_file.dart
@@ -14,6 +14,7 @@
import 'package:yaml/yaml.dart';
import 'io.dart';
+import 'language_version.dart';
import 'package_config.dart';
import 'package_name.dart';
import 'sdk.dart' show sdk;
@@ -268,7 +269,7 @@
name: name,
rootUri: rootUri,
packageUri: p.toUri('lib/'),
- languageVersion: extractLanguageVersion(sdkConstraint),
+ languageVersion: LanguageVersion.fromSdkConstraint(sdkConstraint),
));
}
@@ -277,7 +278,9 @@
name: entrypoint,
rootUri: p.toUri('../'),
packageUri: p.toUri('lib/'),
- languageVersion: extractLanguageVersion(entrypointSdkConstraint),
+ languageVersion: LanguageVersion.fromSdkConstraint(
+ entrypointSdkConstraint,
+ ),
));
}
diff --git a/lib/src/null_safety_analysis.dart b/lib/src/null_safety_analysis.dart
index 204b4b2..ab9777e 100644
--- a/lib/src/null_safety_analysis.dart
+++ b/lib/src/null_safety_analysis.dart
@@ -150,8 +150,7 @@
packageDir = boundSource.getDirectory(dependencyId);
}
- final languageVersion = pubspec.languageVersion;
- if (languageVersion == null || !languageVersion.supportsNullSafety) {
+ if (!pubspec.languageVersion.supportsNullSafety) {
final span =
_tryGetSpanFromYamlMap(pubspec.fields['environment'], 'sdk');
final where = span == null
diff --git a/lib/src/package_config.dart b/lib/src/package_config.dart
index 0c876b3..340860b 100644
--- a/lib/src/package_config.dart
+++ b/lib/src/package_config.dart
@@ -6,6 +6,8 @@
import 'package:pub_semver/pub_semver.dart';
+import 'language_version.dart';
+
/// Contents of a `.dart_tool/package_config.json` file.
class PackageConfig {
/// Version of the configuration in the `.dart_tool/package_config.json` file.
@@ -139,8 +141,6 @@
}..addAll(additionalProperties ?? {});
}
-final _languageVersionPattern = RegExp(r'^\d+\.\d+$');
-
class PackageConfigEntry {
/// Package name.
String name;
@@ -166,7 +166,7 @@
/// in the `pubspec.yaml` for the given package.
///
/// This property is **optional** and may be `null` if not given.
- String languageVersion;
+ LanguageVersion languageVersion;
/// Additional properties not in the specification for the
/// `.dart_tool/package_config.json` file.
@@ -225,12 +225,15 @@
}
}
- final languageVersion = root['languageVersion'];
- if (languageVersion != null) {
- if (languageVersion is! String) {
+ LanguageVersion languageVersion;
+ final languageVersionRaw = root['languageVersion'];
+ if (languageVersionRaw != null) {
+ if (languageVersionRaw is! String) {
_throw('languageVersion', 'must be a string');
}
- if (!_languageVersionPattern.hasMatch(languageVersion)) {
+ try {
+ languageVersion = LanguageVersion.parse(languageVersionRaw);
+ } on FormatException {
_throw('languageVersion', 'must be on the form <major>.<minor>');
}
}
@@ -248,29 +251,6 @@
'name': name,
'rootUri': rootUri.toString(),
if (packageUri != null) 'packageUri': packageUri?.toString(),
- if (languageVersion != null) 'languageVersion': languageVersion,
+ if (languageVersion != null) 'languageVersion': '$languageVersion',
}..addAll(additionalProperties ?? {});
}
-
-/// Extract the _language version_ from an SDK constraint from `pubspec.yaml`.
-///
-/// This returns `null` if there is no language version.
-String extractLanguageVersion(VersionConstraint c) {
- Version minVersion;
- if (c == null || c.isEmpty) {
- return null;
- } else if (c is Version) {
- minVersion = c;
- } else if (c is VersionRange) {
- minVersion = c.min;
- } else if (c is VersionUnion) {
- // `ranges` is non-empty and sorted.
- minVersion = c.ranges.first.min;
- } else {
- throw ArgumentError('Unknown VersionConstraint type $c.');
- }
- if (minVersion == null) {
- return null;
- }
- return '${minVersion.major}.${minVersion.minor}';
-}
diff --git a/lib/src/pubspec.dart b/lib/src/pubspec.dart
index 8a9ad5c..a8c3ed2 100644
--- a/lib/src/pubspec.dart
+++ b/lib/src/pubspec.dart
@@ -447,15 +447,8 @@
name == null && version == Version.none && dependencies.isEmpty;
/// The language version implied by the sdk constraint.
- ///
- /// Given no or unbounded constraint we assume language version 1.0.
- LanguageVersion get languageVersion {
- final constraint = originalDartSdkConstraint;
- if (constraint is VersionRange && constraint.min != null) {
- return LanguageVersion.fromVersionRange(constraint);
- }
- return LanguageVersion(1, 0);
- }
+ LanguageVersion get languageVersion =>
+ LanguageVersion.fromSdkConstraint(originalDartSdkConstraint);
/// Loads the pubspec for a package located in [packageDir].
///
diff --git a/lib/src/validator/relative_version_numbering.dart b/lib/src/validator/relative_version_numbering.dart
index 1eff1e0..fa3ae53 100644
--- a/lib/src/validator/relative_version_numbering.dart
+++ b/lib/src/validator/relative_version_numbering.dart
@@ -4,14 +4,10 @@
import 'dart:async';
-import 'package:pub_semver/pub_semver.dart';
-
import '../entrypoint.dart';
import '../exceptions.dart';
-import '../language_version.dart';
import '../null_safety_analysis.dart';
import '../package_name.dart';
-import '../pubspec.dart';
import '../validator.dart';
/// Gives a warning when publishing a new version, if the latest published
@@ -45,8 +41,9 @@
final previousPubspec =
await hostedSource.bind(entrypoint.cache).describe(previousVersion);
- final currentOptedIn = _optedIntoNullSafety(entrypoint.root.pubspec);
- final previousOptedIn = _optedIntoNullSafety(previousPubspec);
+ final currentOptedIn =
+ entrypoint.root.pubspec.languageVersion.supportsNullSafety;
+ final previousOptedIn = previousPubspec.languageVersion.supportsNullSafety;
if (currentOptedIn && !previousOptedIn) {
hints.add(
@@ -61,21 +58,4 @@
'See $semverUrl for information about versioning.');
}
}
-
- static bool _optedIntoNullSafety(Pubspec pubspec) {
- final sdkConstraint = pubspec.originalDartSdkConstraint;
-
- /// If the sdk constraint is not a `VersionRange` something is wrong, and
- /// we cannot deduce the language version.
- ///
- /// This will hopefully be detected elsewhere.
- ///
- /// A single `Version` is also a `VersionRange`.
- if (sdkConstraint is! VersionRange) return false;
- final constraintMin = (sdkConstraint as VersionRange).min;
-
- if (constraintMin == null) return false;
-
- return LanguageVersion.fromVersionRange(sdkConstraint).supportsNullSafety;
- }
}
diff --git a/test/descriptor.dart b/test/descriptor.dart
index 6265531..2bcd14c 100644
--- a/test/descriptor.dart
+++ b/test/descriptor.dart
@@ -6,6 +6,7 @@
import 'dart:convert';
import 'package:oauth2/oauth2.dart' as oauth2;
+import 'package:pub/src/language_version.dart';
import 'package:pub/src/package_config.dart';
import 'package:test_descriptor/test_descriptor.dart';
import 'package:meta/meta.dart';
@@ -257,7 +258,8 @@
name: name,
rootUri: rootUri,
packageUri: Uri(path: 'lib/'),
- languageVersion: languageVersion,
+ languageVersion:
+ languageVersion != null ? LanguageVersion.parse(languageVersion) : null,
);
}
diff --git a/test/package_config_file_test.dart b/test/package_config_file_test.dart
index a08bc20..a1450ca 100644
--- a/test/package_config_file_test.dart
+++ b/test/package_config_file_test.dart
@@ -32,14 +32,17 @@
d.packageConfigEntry(
name: 'foo',
version: '1.2.3',
+ languageVersion: '2.7',
),
d.packageConfigEntry(
name: 'bar',
version: '3.2.1',
+ languageVersion: '2.7',
),
d.packageConfigEntry(
name: 'baz',
version: '2.2.2',
+ languageVersion: '2.7',
),
d.packageConfigEntry(
name: 'myapp',
@@ -69,6 +72,7 @@
d.packageConfigEntry(
name: 'notFoo',
version: '9.9.9',
+ languageVersion: '2.7',
),
]),
]);
@@ -82,14 +86,17 @@
d.packageConfigEntry(
name: 'foo',
version: '1.2.3',
+ languageVersion: '2.7',
),
d.packageConfigEntry(
name: 'bar',
version: '3.2.1',
+ languageVersion: '2.7',
),
d.packageConfigEntry(
name: 'baz',
version: '2.2.2',
+ languageVersion: '2.7',
),
d.packageConfigEntry(
name: 'myapp',
@@ -154,10 +161,12 @@
d.packageConfigEntry(
name: 'foo',
version: '1.2.3',
+ languageVersion: '2.7',
),
d.packageConfigEntry(
name: 'baz',
path: '../local_baz',
+ languageVersion: '2.7',
),
d.packageConfigEntry(
name: 'myapp',
@@ -213,14 +222,14 @@
]).validate();
});
- test('package_config.json has no default language version', () async {
+ test('package_config.json has 2.7 default language version', () async {
await servePackages((builder) {
builder.serve(
'foo',
'1.2.3',
pubspec: {
'environment': {
- 'sdk': '>=0.0.1 <=0.2.2+2', // tests runs with '0.1.2+3'
+ 'sdk': 'any',
},
},
contents: [d.dir('lib', [])],
@@ -244,7 +253,7 @@
d.packageConfigEntry(
name: 'foo',
version: '1.2.3',
- languageVersion: '0.0',
+ languageVersion: '2.7',
),
d.packageConfigEntry(
name: 'myapp',