language_version.dart 4.46 KB
Newer Older
1 2 3 4
// 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.

5 6
import 'dart:async';

7 8 9
import 'package:file/file.dart';
import 'package:package_config/package_config.dart';

10 11
import '../base/version.dart';

12
final RegExp _languageVersion = RegExp(r'\/\/\s*@dart\s*=\s*([0-9])\.([0-9]+)');
13 14 15 16
final RegExp _declarationEnd = RegExp('(import)|(library)|(part)');
const String _blockCommentStart = '/*';
const String _blockCommentEnd = '*/';

17 18 19
/// The first language version where null safety was available by default.
final LanguageVersion nullSafeVersion = LanguageVersion(2, 12);

20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
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;
}

42
/// Attempts to read the language version of a dart [file].
43 44 45
///
/// If this is not present, falls back to the language version defined in
/// [package]. If [package] is not provided and there is no
46
/// language version header, returns 2.12. This does not specifically check
47 48 49 50
/// 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
51
LanguageVersion determineLanguageVersion(File file, Package? package, String flutterRoot) {
52
  int blockCommentDepth = 0;
53 54 55 56 57 58 59
  // 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 {
60
    return currentLanguageVersion(file.fileSystem, flutterRoot);
61 62 63
  }

  for (final String line in lines) {
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88
    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.
89
    final Match? match = _languageVersion.matchAsPrefix(trimmedLine);
90
    if (match != null) {
91 92
      final String rawMajor = match.group(1) ?? '';
      final String rawMinor = match.group(2) ?? '';
93 94 95 96 97 98 99 100 101
      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;
      }
102 103 104 105 106 107 108 109 110 111 112
    }

    // 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) {
113
    return package.languageVersion ?? currentLanguageVersion(file.fileSystem, flutterRoot);
114
  }
115 116
  // Default to current version.
  return currentLanguageVersion(file.fileSystem, flutterRoot);
117
}