118 lines
4.5 KiB
Dart
118 lines
4.5 KiB
Dart
// Copyright 2014 The Flutter Authors. 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 '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 = '/*';
|
|
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
|
|
/// [package]. If [package] is not provided and there is no
|
|
/// language version header, returns 2.12. This does not specifically check
|
|
/// for language declarations other than library, part, or import.
|
|
///
|
|
/// 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, 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
|
|
// message.
|
|
List<String> lines;
|
|
try {
|
|
lines = file.readAsLinesSync();
|
|
} on FileSystemException {
|
|
return currentLanguageVersion(file.fileSystem, flutterRoot);
|
|
}
|
|
|
|
for (final String line in lines) {
|
|
final String trimmedLine = line.trim();
|
|
if (trimmedLine.isEmpty) {
|
|
continue;
|
|
}
|
|
// Check for the start or end of a block comment. Within a block
|
|
// comment, all language version declarations are ignored. Block
|
|
// comments can be nested, and the start or end may occur on
|
|
// the same line. This does not handle the case of invalid
|
|
// block comment combinations like `*/ /*` since that will cause
|
|
// a compilation error anyway.
|
|
bool sawBlockComment = false;
|
|
final int startMatches = _blockCommentStart.allMatches(trimmedLine).length;
|
|
final int endMatches = _blockCommentEnd.allMatches(trimmedLine).length;
|
|
if (startMatches > 0) {
|
|
blockCommentDepth += startMatches;
|
|
sawBlockComment = true;
|
|
}
|
|
if (endMatches > 0) {
|
|
blockCommentDepth -= endMatches;
|
|
sawBlockComment = true;
|
|
}
|
|
if (blockCommentDepth != 0 || sawBlockComment) {
|
|
continue;
|
|
}
|
|
// Check for a match with the language version.
|
|
final Match? match = _languageVersion.matchAsPrefix(trimmedLine);
|
|
if (match != null) {
|
|
final String rawMajor = match.group(1) ?? '';
|
|
final String rawMinor = match.group(2) ?? '';
|
|
try {
|
|
final int major = int.parse(rawMajor);
|
|
final int minor = int.parse(rawMinor);
|
|
return LanguageVersion(major, minor);
|
|
} on FormatException {
|
|
// Language comment was invalid in a way that the regexp did not
|
|
// anticipate.
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Check for a declaration which ends the search for a language
|
|
// version.
|
|
if (_declarationEnd.matchAsPrefix(trimmedLine) != null) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
// If the language version cannot be found, use the package version.
|
|
if (package != null) {
|
|
return package.languageVersion ?? currentLanguageVersion(file.fileSystem, flutterRoot);
|
|
}
|
|
// Default to current version.
|
|
return currentLanguageVersion(file.fileSystem, flutterRoot);
|
|
}
|