Unverified Commit 35bd77bb authored by Jonah Williams's avatar Jonah Williams Committed by GitHub

[flutter_tools] separate concept of null safe language version from current...

[flutter_tools] separate concept of null safe language version from current language version (#79052)
parent 971881c8
......@@ -14,6 +14,7 @@ import '../../artifacts.dart';
import '../../base/file_system.dart';
import '../../base/io.dart';
import '../../build_info.dart';
import '../../cache.dart';
import '../../dart/language_version.dart';
import '../../dart/package_map.dart';
import '../../globals.dart' as globals;
......@@ -103,6 +104,7 @@ class WebEntrypointTarget extends Target {
final LanguageVersion languageVersion = determineLanguageVersion(
environment.fileSystem.file(targetFile),
packageConfig[flutterProject.manifest.appName],
Cache.flutterRoot,
);
// Use the PackageConfig to find the correct package-scheme import path
......
......@@ -2,9 +2,13 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:async';
import 'package:file/file.dart';
import 'package:package_config/package_config.dart';
import '../base/version.dart';
final RegExp _languageVersion = RegExp(r'\/\/\s*@dart\s*=\s*([0-9])\.([0-9]+)');
final RegExp _declarationEnd = RegExp('(import)|(library)|(part)');
const String _blockCommentStart = '/*';
......@@ -13,6 +17,28 @@ const String _blockCommentEnd = '*/';
/// The first language version where null safety was available by default.
final LanguageVersion nullSafeVersion = LanguageVersion(2, 12);
LanguageVersion? _currentLanguageVersion;
/// Lookup the current Dart language version.
LanguageVersion currentLanguageVersion(FileSystem fileSystem, String flutterRoot) {
if (_currentLanguageVersion != null) {
return _currentLanguageVersion!;
}
// Either reading the file or parsing the version could fail on a corrupt Dart SDK.
// let it crash so it shows up in crash logging.
final File versionFile = fileSystem.file(fileSystem.path.join(flutterRoot, 'bin', 'cache', 'dart-sdk', 'version'));
if (!versionFile.existsSync() && _inUnitTest()) {
return LanguageVersion(2, 12);
}
final Version version = Version.parse(versionFile.readAsStringSync())!;
return _currentLanguageVersion = LanguageVersion(version.major, version.minor);
}
// Whether the tool is executing in a unit test.
bool _inUnitTest() {
return Zone.current[#test.declarer] != null;
}
/// Attempts to read the language version of a dart [file].
///
/// If this is not present, falls back to the language version defined in
......@@ -22,7 +48,7 @@ final LanguageVersion nullSafeVersion = LanguageVersion(2, 12);
///
/// The specification for the language version tag is defined at:
/// https://github.com/dart-lang/language/blob/master/accepted/future-releases/language-versioning/feature-specification.md#individual-library-language-version-override
LanguageVersion determineLanguageVersion(File file, Package package) {
LanguageVersion determineLanguageVersion(File file, Package package, String flutterRoot) {
int blockCommentDepth = 0;
// If reading the file fails, default to a null-safe version. The
// command will likely fail later in the process with a better error
......@@ -31,7 +57,7 @@ LanguageVersion determineLanguageVersion(File file, Package package) {
try {
lines = file.readAsLinesSync();
} on FileSystemException {
return nullSafeVersion;
return currentLanguageVersion(file.fileSystem, flutterRoot);
}
for (final String line in lines) {
......@@ -84,8 +110,8 @@ LanguageVersion determineLanguageVersion(File file, Package package) {
// If the language version cannot be found, use the package version.
if (package != null) {
return package.languageVersion ?? nullSafeVersion;
return package.languageVersion ?? currentLanguageVersion(file.fileSystem, flutterRoot);
}
// Default to 2.12
return nullSafeVersion;
// Default to current version.
return currentLanguageVersion(file.fileSystem, flutterRoot);
}
......@@ -25,6 +25,7 @@ import '../base/time.dart';
import '../base/utils.dart';
import '../build_info.dart';
import '../build_system/targets/web.dart';
import '../cache.dart';
import '../dart/language_version.dart';
import '../devfs.dart';
import '../device.dart';
......@@ -678,6 +679,7 @@ class ResidentWebRunner extends ResidentRunner {
final LanguageVersion languageVersion = determineLanguageVersion(
_fileSystem.file(mainUri),
packageConfig[flutterProject.manifest.appName],
Cache.flutterRoot,
);
final String entrypoint = <String>[
......
......@@ -892,6 +892,7 @@ abstract class FlutterCommand extends Command<void> {
final LanguageVersion languageVersion = determineLanguageVersion(
entrypointFile,
packageConfig.packageOf(entrypointFile.absolute.uri),
Cache.flutterRoot,
);
// Extra frontend options are only provided if explicitly
// requested.
......
......@@ -14,6 +14,7 @@ import 'package:test_core/src/platform.dart'; // ignore: implementation_imports
import '../base/common.dart';
import '../base/file_system.dart';
import '../base/io.dart';
import '../cache.dart';
import '../compile.dart';
import '../convert.dart';
import '../dart/language_version.dart';
......@@ -512,6 +513,7 @@ class FlutterPlatform extends PlatformPlugin {
final LanguageVersion languageVersion = determineLanguageVersion(
file,
packageConfig[flutterProject?.manifest?.appName],
Cache.flutterRoot,
);
return generateTestBootstrap(
testUrl: testUrl,
......
......@@ -16,6 +16,7 @@ import '../base/logger.dart';
import '../base/platform.dart';
import '../build_info.dart';
import '../bundle.dart';
import '../cache.dart';
import '../compile.dart';
import '../dart/language_version.dart';
import '../web/bootstrap.dart';
......@@ -63,7 +64,7 @@ class WebTestCompiler {
}
} else if (buildInfo.nullSafetyMode == NullSafetyMode.sound) {
platformDillArtifact = Artifact.webPlatformSoundKernelDill;
languageVersion = nullSafeVersion;
languageVersion = currentLanguageVersion(_fileSystem, Cache.flutterRoot);
if (!extraFrontEndOptions.contains('--sound-null-safety')) {
extraFrontEndOptions.add('--sound-null-safety');
}
......
......@@ -6,16 +6,25 @@
import 'package:file/memory.dart';
import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/convert.dart';
import 'package:flutter_tools/src/dart/language_version.dart';
import 'package:package_config/package_config.dart';
import 'package:test/fake.dart';
import '../../src/common.dart';
const String flutterRoot = '';
const String testVersionString = '2.13';
final LanguageVersion testCurrentLanguageVersion = LanguageVersion(2, 13);
void setUpLanguageVersion(FileSystem fileSystem, [String version = testVersionString]) {
fileSystem.file(fileSystem.path.join('bin', 'cache', 'dart-sdk', 'version'))
..createSync(recursive: true)
..writeAsStringSync(version);
}
void main() {
testWithoutContext('detects language version in comment', () {
final FileSystem fileSystem = MemoryFileSystem.test();
setUpLanguageVersion(fileSystem);
final File file = fileSystem.file('example.dart')
..writeAsStringSync('''
// Some license
......@@ -23,11 +32,12 @@ void main() {
// @dart = 2.9
''');
expect(determineLanguageVersion(file, null), LanguageVersion(2, 9));
expect(determineLanguageVersion(file, null, flutterRoot), LanguageVersion(2, 9));
});
testWithoutContext('detects language version in comment without spacing', () {
final FileSystem fileSystem = MemoryFileSystem.test();
setUpLanguageVersion(fileSystem);
final File file = fileSystem.file('example.dart')
..writeAsStringSync('''
// Some license
......@@ -35,11 +45,12 @@ void main() {
// @dart=2.9
''');
expect(determineLanguageVersion(file, null), LanguageVersion(2, 9));
expect(determineLanguageVersion(file, null, flutterRoot), LanguageVersion(2, 9));
});
testWithoutContext('detects language version in comment with more numbers', () {
final FileSystem fileSystem = MemoryFileSystem.test();
setUpLanguageVersion(fileSystem);
final File file = fileSystem.file('example.dart')
..writeAsStringSync('''
// Some license
......@@ -47,11 +58,12 @@ void main() {
// @dart=2.12
''');
expect(determineLanguageVersion(file, null), LanguageVersion(2, 12));
expect(determineLanguageVersion(file, null, flutterRoot), nullSafeVersion);
});
testWithoutContext('does not detect invalid language version', () {
final FileSystem fileSystem = MemoryFileSystem.test();
setUpLanguageVersion(fileSystem);
final File file = fileSystem.file('example.dart')
..writeAsStringSync('''
// Some license
......@@ -59,7 +71,7 @@ void main() {
// @dart
''');
expect(determineLanguageVersion(file, null), LanguageVersion(2, 12));
expect(determineLanguageVersion(file, null, flutterRoot), testCurrentLanguageVersion);
});
testWithoutContext('detects language version with leading whitespace', () {
......@@ -71,7 +83,7 @@ void main() {
// @dart = 2.9
''');
expect(determineLanguageVersion(file, null), LanguageVersion(2, 9));
expect(determineLanguageVersion(file, null, flutterRoot), LanguageVersion(2, 9));
});
testWithoutContext('detects language version with tabs', () {
......@@ -83,7 +95,7 @@ void main() {
//\t@dart = 2.9
''');
expect(determineLanguageVersion(file, null), LanguageVersion(2, 9));
expect(determineLanguageVersion(file, null, flutterRoot), LanguageVersion(2, 9));
});
testWithoutContext('detects language version with tons of whitespace', () {
......@@ -95,11 +107,12 @@ void main() {
// @dart = 2.23
''');
expect(determineLanguageVersion(file, null), LanguageVersion(2, 23));
expect(determineLanguageVersion(file, null, flutterRoot), LanguageVersion(2, 23));
});
testWithoutContext('does not detect language version in dartdoc', () {
final FileSystem fileSystem = MemoryFileSystem.test();
setUpLanguageVersion(fileSystem);
final File file = fileSystem.file('example.dart')
..writeAsStringSync('''
// Some license
......@@ -107,11 +120,12 @@ void main() {
/// @dart = 2.9
''');
expect(determineLanguageVersion(file, null), LanguageVersion(2, 12));
expect(determineLanguageVersion(file, null, flutterRoot), testCurrentLanguageVersion);
});
testWithoutContext('does not detect language version in block comment', () {
final FileSystem fileSystem = MemoryFileSystem.test();
setUpLanguageVersion(fileSystem);
final File file = fileSystem.file('example.dart')
..writeAsStringSync('''
// Some license
......@@ -121,11 +135,12 @@ void main() {
*/
''');
expect(determineLanguageVersion(file, null), LanguageVersion(2, 12));
expect(determineLanguageVersion(file, null, flutterRoot), testCurrentLanguageVersion);
});
testWithoutContext('does not detect language version in nested block comment', () {
final FileSystem fileSystem = MemoryFileSystem.test();
setUpLanguageVersion(fileSystem);
final File file = fileSystem.file('example.dart')
..writeAsStringSync('''
// Some license
......@@ -137,7 +152,7 @@ void main() {
*/
''');
expect(determineLanguageVersion(file, null), LanguageVersion(2, 12));
expect(determineLanguageVersion(file, null, flutterRoot), testCurrentLanguageVersion);
});
testWithoutContext('detects language version after nested block comment', () {
......@@ -152,11 +167,12 @@ void main() {
// @dart = 2.9
''');
expect(determineLanguageVersion(file, null), LanguageVersion(2, 9));
expect(determineLanguageVersion(file, null, flutterRoot), LanguageVersion(2, 9));
});
testWithoutContext('does not crash with unbalanced opening block comments', () {
final FileSystem fileSystem = MemoryFileSystem.test();
setUpLanguageVersion(fileSystem);
final File file = fileSystem.file('example.dart')
..writeAsStringSync('''
// Some license
......@@ -167,11 +183,12 @@ void main() {
// @dart = 2.9
''');
expect(determineLanguageVersion(file, null), LanguageVersion(2, 12));
expect(determineLanguageVersion(file, null, flutterRoot), testCurrentLanguageVersion);
});
testWithoutContext('does not crash with unbalanced closing block comments', () {
final FileSystem fileSystem = MemoryFileSystem.test();
setUpLanguageVersion(fileSystem);
final File file = fileSystem.file('example.dart')
..writeAsStringSync('''
// Some license
......@@ -182,11 +199,12 @@ void main() {
// @dart = 2.9
''');
expect(determineLanguageVersion(file, null), LanguageVersion(2, 12));
expect(determineLanguageVersion(file, null, flutterRoot), testCurrentLanguageVersion);
});
testWithoutContext('does not detect language version in single line block comment', () {
final FileSystem fileSystem = MemoryFileSystem.test();
setUpLanguageVersion(fileSystem);
final File file = fileSystem.file('example.dart')
..writeAsStringSync('''
// Some license
......@@ -194,11 +212,12 @@ void main() {
/* // @dart = 2.9 */
''');
expect(determineLanguageVersion(file, null), LanguageVersion(2, 12));
expect(determineLanguageVersion(file, null, flutterRoot), testCurrentLanguageVersion);
});
testWithoutContext('does not detect language version after import declaration', () {
final FileSystem fileSystem = MemoryFileSystem.test();
setUpLanguageVersion(fileSystem);
final File file = fileSystem.file('example.dart')
..writeAsStringSync('''
// Some license
......@@ -208,11 +227,12 @@ import 'dart:ui' as ui;
// @dart = 2.9
''');
expect(determineLanguageVersion(file, null), LanguageVersion(2, 12));
expect(determineLanguageVersion(file, null, flutterRoot), testCurrentLanguageVersion);
});
testWithoutContext('does not detect language version after part declaration', () {
final FileSystem fileSystem = MemoryFileSystem.test();
setUpLanguageVersion(fileSystem);
final File file = fileSystem.file('example.dart')
..writeAsStringSync('''
// Some license
......@@ -222,7 +242,7 @@ part of 'foo.dart';
// @dart = 2.9
''');
expect(determineLanguageVersion(file, null), LanguageVersion(2, 12));
expect(determineLanguageVersion(file, null, flutterRoot), testCurrentLanguageVersion);
});
testWithoutContext('does not detect language version after library declaration', () {
......@@ -236,7 +256,7 @@ library funstuff;
// @dart = 2.9
''');
expect(determineLanguageVersion(file, null), LanguageVersion(2, 12));
expect(determineLanguageVersion(file, null, flutterRoot), testCurrentLanguageVersion);
});
testWithoutContext('looks up language version from package if not found in file', () {
......@@ -251,10 +271,10 @@ library funstuff;
languageVersion: LanguageVersion(2, 7),
);
expect(determineLanguageVersion(file, package), LanguageVersion(2, 7));
expect(determineLanguageVersion(file, package, flutterRoot), LanguageVersion(2, 7));
});
testWithoutContext('defaults to null safe version if package lookup returns null', () {
testWithoutContext('defaults to current version if package lookup returns null', () {
final FileSystem fileSystem = MemoryFileSystem.test();
final File file = fileSystem.file('example.dart')
..writeAsStringSync('''
......@@ -266,23 +286,29 @@ library funstuff;
languageVersion: null,
);
expect(determineLanguageVersion(file, package), LanguageVersion(2, 12));
expect(determineLanguageVersion(file, package, flutterRoot), testCurrentLanguageVersion);
});
testWithoutContext('Returns null safe error if reading the file throws a FileSystemException', () {
final FileExceptionHandler handler = FileExceptionHandler();
final FileSystem fileSystem = MemoryFileSystem.test(opHandle: handler.opHandle);
setUpLanguageVersion(fileSystem);
final File errorFile = fileSystem.file('foo');
handler.addError(errorFile, FileSystemOp.read, const FileSystemException());
final Package package = Package(
'foo',
Uri.parse('file://foo/'),
languageVersion: LanguageVersion(2, 7),
);
expect(determineLanguageVersion(FakeFile(), package), nullSafeVersion);
expect(determineLanguageVersion(errorFile, package, flutterRoot), testCurrentLanguageVersion);
});
}
class FakeFile extends Fake implements File {
@override
List<String> readAsLinesSync({ Encoding encoding = utf8ForTesting }) {
throw const FileSystemException();
}
testWithoutContext('Can parse Dart language version with pre/post suffix', () {
final FileSystem fileSystem = MemoryFileSystem.test();
setUpLanguageVersion(fileSystem, '2.13.0-150.0.dev');
expect(currentLanguageVersion(fileSystem, flutterRoot), LanguageVersion(2, 13));
});
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment