Issue 42474. Add LibraryElement.languageVersion

Bug: https://github.com/dart-lang/sdk/issues/42474
Change-Id: I316f34b14f01daa517182c1cb7b7c3fb038f0be2
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/152601
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
diff --git a/pkg/analyzer/CHANGELOG.md b/pkg/analyzer/CHANGELOG.md
index 5ee8c86..2dfc781 100644
--- a/pkg/analyzer/CHANGELOG.md
+++ b/pkg/analyzer/CHANGELOG.md
@@ -1,6 +1,8 @@
 ## 0.39.11-dev
 * Deprecated `ClassElement.hasReferenceToSuper`.
   It was used internally, should not be part of API.
+* Deprecated `LibraryElement.languageVersionMajor/minor`.
+  Use `LibraryElement.languageVersion` to access more specific information.
 
 ## 0.39.10
 * Restored the default constructor in internal `SummaryBuilder`,
diff --git a/pkg/analyzer/lib/dart/element/element.dart b/pkg/analyzer/lib/dart/element/element.dart
index 07919a0..dda7250 100644
--- a/pkg/analyzer/lib/dart/element/element.dart
+++ b/pkg/analyzer/lib/dart/element/element.dart
@@ -49,6 +49,7 @@
 import 'package:analyzer/src/generated/utilities_dart.dart';
 import 'package:analyzer/src/task/api/model.dart' show AnalysisTarget;
 import 'package:meta/meta.dart';
+import 'package:pub_semver/pub_semver.dart';
 
 /// An element that represents a class or a mixin. The class can be defined by
 /// either a class declaration (with a class body), a mixin application (without
@@ -1341,10 +1342,15 @@
 
   bool get isNonNullableByDefault;
 
+  /// The language version for this library.
+  LibraryLanguageVersion get languageVersion;
+
   /// The major component of the language version for this library.
+  @Deprecated("Use 'languageVersion'")
   int get languageVersionMajor;
 
   /// The minor component of the language version for this library.
+  @Deprecated("Use 'languageVersion'")
   int get languageVersionMinor;
 
   /// Return the element representing the synthetic function `loadLibrary` that
@@ -1399,6 +1405,24 @@
   DartType toLegacyTypeIfOptOut(DartType type);
 }
 
+class LibraryLanguageVersion {
+  /// The version for the whole package that contains this library.
+  final Version package;
+
+  /// The version specified using `@dart` override, `null` if absent or invalid.
+  final Version override;
+
+  LibraryLanguageVersion({
+    @required this.package,
+    @required this.override,
+  });
+
+  /// The effective language version for the library.
+  Version get effective {
+    return override ?? package;
+  }
+}
+
 /// An element that can be (but is not required to be) defined within a method
 /// or function (an [ExecutableElement]).
 ///
diff --git a/pkg/analyzer/lib/src/dart/analysis/driver.dart b/pkg/analyzer/lib/src/dart/analysis/driver.dart
index a7092a1..3ec888a 100644
--- a/pkg/analyzer/lib/src/dart/analysis/driver.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/driver.dart
@@ -91,7 +91,7 @@
 /// TODO(scheglov) Clean up the list of implicitly analyzed files.
 class AnalysisDriver implements AnalysisDriverGeneric {
   /// The version of data format, should be incremented on every format change.
-  static const int DATA_VERSION = 102;
+  static const int DATA_VERSION = 103;
 
   /// The length of the list returned by [_computeDeclaredVariablesSignature].
   static const int _declaredVariablesSignatureLength = 4;
diff --git a/pkg/analyzer/lib/src/dart/analysis/file_state.dart b/pkg/analyzer/lib/src/dart/analysis/file_state.dart
index 55b247c..0e00b63 100644
--- a/pkg/analyzer/lib/src/dart/analysis/file_state.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/file_state.dart
@@ -12,6 +12,7 @@
 import 'package:analyzer/dart/ast/ast.dart';
 import 'package:analyzer/dart/ast/standard_ast_factory.dart';
 import 'package:analyzer/dart/ast/token.dart';
+import 'package:analyzer/dart/element/element.dart';
 import 'package:analyzer/error/error.dart';
 import 'package:analyzer/error/listener.dart';
 import 'package:analyzer/file_system/file_system.dart';
@@ -562,11 +563,20 @@
 
   CompilationUnit _createEmptyCompilationUnit() {
     var token = Token.eof(0);
-    return astFactory.compilationUnit(
+    var unit = astFactory.compilationUnit(
       beginToken: token,
       endToken: token,
       featureSet: _contextFeatureSet,
-    )..lineInfo = LineInfo(const <int>[0]);
+    ) as CompilationUnitImpl;
+
+    unit.lineInfo = LineInfo(const <int>[0]);
+
+    unit.languageVersion = LibraryLanguageVersion(
+      package: _packageLanguageVersion,
+      override: null,
+    );
+
+    return unit;
   }
 
   /**
@@ -678,13 +688,11 @@
     LanguageVersionToken versionToken,
     AnalysisErrorListener errorListener,
   ) {
-    var languageVersion = _packageLanguageVersion;
-
+    Version overrideVersion;
     if (versionToken != null) {
+      overrideVersion = Version(versionToken.major, versionToken.minor, 0);
       var latestVersion = ExperimentStatus.currentVersion;
-      if (versionToken.major > latestVersion.major ||
-          versionToken.major == latestVersion.major &&
-              versionToken.minor > latestVersion.minor) {
+      if (overrideVersion > latestVersion) {
         errorListener.onError(
           AnalysisError(
             source,
@@ -694,15 +702,15 @@
             [latestVersion.major, latestVersion.minor],
           ),
         );
-        // Fall-through, use the package language version.
-      } else {
-        languageVersion = Version(versionToken.major, versionToken.minor, 0);
+        overrideVersion = null;
       }
     }
 
     var unitImpl = unit as CompilationUnitImpl;
-    unitImpl.languageVersionMajor = languageVersion.major;
-    unitImpl.languageVersionMinor = languageVersion.minor;
+    unitImpl.languageVersion = LibraryLanguageVersion(
+      package: _packageLanguageVersion,
+      override: overrideVersion,
+    );
   }
 
   static UnlinkedUnit2Builder serializeAstUnlinked2(CompilationUnit unit) {
diff --git a/pkg/analyzer/lib/src/dart/ast/ast.dart b/pkg/analyzer/lib/src/dart/ast/ast.dart
index 5716d1a..ab4cd9e 100644
--- a/pkg/analyzer/lib/src/dart/ast/ast.dart
+++ b/pkg/analyzer/lib/src/dart/ast/ast.dart
@@ -2019,11 +2019,8 @@
   @override
   LineInfo lineInfo;
 
-  /// The major component of the actual language version (not just override).
-  int languageVersionMajor;
-
-  /// The minor component of the actual language version (not just override).
-  int languageVersionMinor;
+  /// The language version information.
+  LibraryLanguageVersion languageVersion;
 
   @override
   final FeatureSet featureSet;
diff --git a/pkg/analyzer/lib/src/dart/element/element.dart b/pkg/analyzer/lib/src/dart/element/element.dart
index c8cecf0..c840b35 100644
--- a/pkg/analyzer/lib/src/dart/element/element.dart
+++ b/pkg/analyzer/lib/src/dart/element/element.dart
@@ -2639,11 +2639,8 @@
   /// The length of the element's code, or `null` if the element is synthetic.
   int _codeLength;
 
-  /// The major component of the language version.
-  int _languageVersionMajor;
-
-  /// The minor component of the language version.
-  int _languageVersionMinor;
+  /// The language version for the library.
+  LibraryLanguageVersion _languageVersion;
 
   /// Initialize a newly created element to have the given [name] at the given
   /// [_nameOffset].
@@ -5502,29 +5499,31 @@
   ElementKind get kind => ElementKind.LIBRARY;
 
   @override
-  int get languageVersionMajor {
-    if (_languageVersionMajor != null) return _languageVersionMajor;
+  LibraryLanguageVersion get languageVersion {
+    if (_languageVersion != null) return _languageVersion;
 
     if (linkedNode != null) {
-      _languageVersionMajor = linkedContext.getLanguageVersionMajor(linkedNode);
-      return _languageVersionMajor;
+      _languageVersion = linkedContext.getLanguageVersion(linkedNode);
+      return _languageVersion;
     }
 
-    _languageVersionMajor = ExperimentStatus.currentVersion.major;
-    return _languageVersionMajor;
+    _languageVersion = LibraryLanguageVersion(
+      package: ExperimentStatus.currentVersion,
+      override: null,
+    );
+    return _languageVersion;
   }
 
+  @Deprecated("Use 'languageVersion'")
+  @override
+  int get languageVersionMajor {
+    return languageVersion.effective.major;
+  }
+
+  @Deprecated("Use 'languageVersion'")
   @override
   int get languageVersionMinor {
-    if (_languageVersionMinor != null) return _languageVersionMinor;
-
-    if (linkedNode != null) {
-      _languageVersionMinor = linkedContext.getLanguageVersionMinor(linkedNode);
-      return _languageVersionMinor;
-    }
-
-    _languageVersionMinor = ExperimentStatus.currentVersion.minor;
-    return _languageVersionMinor;
+    return languageVersion.effective.minor;
   }
 
   @override
@@ -5676,11 +5675,6 @@
     return getTypeFromParts(className, _definingCompilationUnit, _parts);
   }
 
-  void setLanguageVersion(int major, int minor) {
-    _languageVersionMajor = major;
-    _languageVersionMinor = minor;
-  }
-
   /// Set whether the library has the given [capability] to
   /// correspond to the given [value].
   void setResolutionCapability(
diff --git a/pkg/analyzer/lib/src/dart/micro/library_graph.dart b/pkg/analyzer/lib/src/dart/micro/library_graph.dart
index f4b50dd..059515c 100644
--- a/pkg/analyzer/lib/src/dart/micro/library_graph.dart
+++ b/pkg/analyzer/lib/src/dart/micro/library_graph.dart
@@ -9,11 +9,14 @@
 import 'package:analyzer/dart/analysis/features.dart';
 import 'package:analyzer/dart/ast/ast.dart';
 import 'package:analyzer/dart/ast/token.dart';
+import 'package:analyzer/dart/element/element.dart';
 import 'package:analyzer/error/listener.dart';
 import 'package:analyzer/file_system/file_system.dart';
+import 'package:analyzer/src/dart/analysis/experiments.dart';
 import 'package:analyzer/src/dart/analysis/feature_set_provider.dart';
 import 'package:analyzer/src/dart/analysis/performance_logger.dart';
 import 'package:analyzer/src/dart/analysis/unlinked_api_signature.dart';
+import 'package:analyzer/src/dart/ast/ast.dart';
 import 'package:analyzer/src/dart/micro/cider_byte_store.dart';
 import 'package:analyzer/src/dart/scanner/reader.dart';
 import 'package:analyzer/src/dart/scanner/scanner.dart';
@@ -154,6 +157,13 @@
     // to clear it explicitly once we are done using it for this file.
     StringToken.canonicalizer.clear();
 
+    // TODO(scheglov) Use actual versions.
+    var unitImpl = unit as CompilationUnitImpl;
+    unitImpl.languageVersion = LibraryLanguageVersion(
+      package: ExperimentStatus.currentVersion,
+      override: null,
+    );
+
     return unit;
   }
 
diff --git a/pkg/analyzer/lib/src/summary/format.dart b/pkg/analyzer/lib/src/summary/format.dart
index 125e54a..8680e93 100644
--- a/pkg/analyzer/lib/src/summary/format.dart
+++ b/pkg/analyzer/lib/src/summary/format.dart
@@ -4214,6 +4214,230 @@
   String toString() => convert.json.encode(toJson());
 }
 
+class LinkedLanguageVersionBuilder extends Object
+    with _LinkedLanguageVersionMixin
+    implements idl.LinkedLanguageVersion {
+  int _major;
+  int _minor;
+
+  @override
+  int get major => _major ??= 0;
+
+  set major(int value) {
+    assert(value == null || value >= 0);
+    this._major = value;
+  }
+
+  @override
+  int get minor => _minor ??= 0;
+
+  set minor(int value) {
+    assert(value == null || value >= 0);
+    this._minor = value;
+  }
+
+  LinkedLanguageVersionBuilder({int major, int minor})
+      : _major = major,
+        _minor = minor;
+
+  /// Flush [informative] data recursively.
+  void flushInformative() {}
+
+  /// Accumulate non-[informative] data into [signature].
+  void collectApiSignature(api_sig.ApiSignature signature) {
+    signature.addInt(this._major ?? 0);
+    signature.addInt(this._minor ?? 0);
+  }
+
+  fb.Offset finish(fb.Builder fbBuilder) {
+    fbBuilder.startTable();
+    if (_major != null && _major != 0) {
+      fbBuilder.addUint32(0, _major);
+    }
+    if (_minor != null && _minor != 0) {
+      fbBuilder.addUint32(1, _minor);
+    }
+    return fbBuilder.endTable();
+  }
+}
+
+class _LinkedLanguageVersionReader
+    extends fb.TableReader<_LinkedLanguageVersionImpl> {
+  const _LinkedLanguageVersionReader();
+
+  @override
+  _LinkedLanguageVersionImpl createObject(fb.BufferContext bc, int offset) =>
+      _LinkedLanguageVersionImpl(bc, offset);
+}
+
+class _LinkedLanguageVersionImpl extends Object
+    with _LinkedLanguageVersionMixin
+    implements idl.LinkedLanguageVersion {
+  final fb.BufferContext _bc;
+  final int _bcOffset;
+
+  _LinkedLanguageVersionImpl(this._bc, this._bcOffset);
+
+  int _major;
+  int _minor;
+
+  @override
+  int get major {
+    _major ??= const fb.Uint32Reader().vTableGet(_bc, _bcOffset, 0, 0);
+    return _major;
+  }
+
+  @override
+  int get minor {
+    _minor ??= const fb.Uint32Reader().vTableGet(_bc, _bcOffset, 1, 0);
+    return _minor;
+  }
+}
+
+abstract class _LinkedLanguageVersionMixin
+    implements idl.LinkedLanguageVersion {
+  @override
+  Map<String, Object> toJson() {
+    Map<String, Object> _result = <String, Object>{};
+    if (major != 0) {
+      _result["major"] = major;
+    }
+    if (minor != 0) {
+      _result["minor"] = minor;
+    }
+    return _result;
+  }
+
+  @override
+  Map<String, Object> toMap() => {
+        "major": major,
+        "minor": minor,
+      };
+
+  @override
+  String toString() => convert.json.encode(toJson());
+}
+
+class LinkedLibraryLanguageVersionBuilder extends Object
+    with _LinkedLibraryLanguageVersionMixin
+    implements idl.LinkedLibraryLanguageVersion {
+  LinkedLanguageVersionBuilder _override2;
+  LinkedLanguageVersionBuilder _package;
+
+  @override
+  LinkedLanguageVersionBuilder get override2 => _override2;
+
+  set override2(LinkedLanguageVersionBuilder value) {
+    this._override2 = value;
+  }
+
+  @override
+  LinkedLanguageVersionBuilder get package => _package;
+
+  set package(LinkedLanguageVersionBuilder value) {
+    this._package = value;
+  }
+
+  LinkedLibraryLanguageVersionBuilder(
+      {LinkedLanguageVersionBuilder override2,
+      LinkedLanguageVersionBuilder package})
+      : _override2 = override2,
+        _package = package;
+
+  /// Flush [informative] data recursively.
+  void flushInformative() {
+    _override2?.flushInformative();
+    _package?.flushInformative();
+  }
+
+  /// Accumulate non-[informative] data into [signature].
+  void collectApiSignature(api_sig.ApiSignature signature) {
+    signature.addBool(this._package != null);
+    this._package?.collectApiSignature(signature);
+    signature.addBool(this._override2 != null);
+    this._override2?.collectApiSignature(signature);
+  }
+
+  fb.Offset finish(fb.Builder fbBuilder) {
+    fb.Offset offset_override2;
+    fb.Offset offset_package;
+    if (_override2 != null) {
+      offset_override2 = _override2.finish(fbBuilder);
+    }
+    if (_package != null) {
+      offset_package = _package.finish(fbBuilder);
+    }
+    fbBuilder.startTable();
+    if (offset_override2 != null) {
+      fbBuilder.addOffset(1, offset_override2);
+    }
+    if (offset_package != null) {
+      fbBuilder.addOffset(0, offset_package);
+    }
+    return fbBuilder.endTable();
+  }
+}
+
+class _LinkedLibraryLanguageVersionReader
+    extends fb.TableReader<_LinkedLibraryLanguageVersionImpl> {
+  const _LinkedLibraryLanguageVersionReader();
+
+  @override
+  _LinkedLibraryLanguageVersionImpl createObject(
+          fb.BufferContext bc, int offset) =>
+      _LinkedLibraryLanguageVersionImpl(bc, offset);
+}
+
+class _LinkedLibraryLanguageVersionImpl extends Object
+    with _LinkedLibraryLanguageVersionMixin
+    implements idl.LinkedLibraryLanguageVersion {
+  final fb.BufferContext _bc;
+  final int _bcOffset;
+
+  _LinkedLibraryLanguageVersionImpl(this._bc, this._bcOffset);
+
+  idl.LinkedLanguageVersion _override2;
+  idl.LinkedLanguageVersion _package;
+
+  @override
+  idl.LinkedLanguageVersion get override2 {
+    _override2 ??=
+        const _LinkedLanguageVersionReader().vTableGet(_bc, _bcOffset, 1, null);
+    return _override2;
+  }
+
+  @override
+  idl.LinkedLanguageVersion get package {
+    _package ??=
+        const _LinkedLanguageVersionReader().vTableGet(_bc, _bcOffset, 0, null);
+    return _package;
+  }
+}
+
+abstract class _LinkedLibraryLanguageVersionMixin
+    implements idl.LinkedLibraryLanguageVersion {
+  @override
+  Map<String, Object> toJson() {
+    Map<String, Object> _result = <String, Object>{};
+    if (override2 != null) {
+      _result["override2"] = override2.toJson();
+    }
+    if (package != null) {
+      _result["package"] = package.toJson();
+    }
+    return _result;
+  }
+
+  @override
+  Map<String, Object> toMap() => {
+        "override2": override2,
+        "package": package,
+      };
+
+  @override
+  String toString() => convert.json.encode(toJson());
+}
+
 class LinkedNodeBuilder extends Object
     with _LinkedNodeMixin
     implements idl.LinkedNode {
@@ -4235,7 +4459,7 @@
   List<String> _variantField_33;
   idl.LinkedNodeCommentType _variantField_29;
   List<LinkedNodeBuilder> _variantField_3;
-  int _variantField_16;
+  LinkedLibraryLanguageVersionBuilder _variantField_40;
   LinkedNodeBuilder _variantField_10;
   idl.LinkedNodeFormalParameterKind _variantField_26;
   double _variantField_21;
@@ -4245,6 +4469,7 @@
   int _flags;
   String _variantField_1;
   int _variantField_36;
+  int _variantField_16;
   String _variantField_30;
   LinkedNodeBuilder _variantField_14;
   idl.LinkedNodeKind _kind;
@@ -6414,12 +6639,6 @@
   }
 
   @override
-  int get compilationUnit_languageVersionMajor {
-    assert(kind == idl.LinkedNodeKind.compilationUnit);
-    return _variantField_15 ??= 0;
-  }
-
-  @override
   int get constructorName_element {
     assert(kind == idl.LinkedNodeKind.constructorName);
     return _variantField_15 ??= 0;
@@ -6491,12 +6710,6 @@
     _variantField_15 = value;
   }
 
-  set compilationUnit_languageVersionMajor(int value) {
-    assert(kind == idl.LinkedNodeKind.compilationUnit);
-    assert(value == null || value >= 0);
-    _variantField_15 = value;
-  }
-
   set constructorName_element(int value) {
     assert(kind == idl.LinkedNodeKind.constructorName);
     assert(value == null || value >= 0);
@@ -6888,28 +7101,16 @@
   }
 
   @override
-  int get compilationUnit_languageVersionMinor {
+  LinkedLibraryLanguageVersionBuilder get compilationUnit_languageVersion {
     assert(kind == idl.LinkedNodeKind.compilationUnit);
-    return _variantField_16 ??= 0;
+    return _variantField_40;
   }
 
-  @override
-  int get integerLiteral_value {
-    assert(kind == idl.LinkedNodeKind.integerLiteral);
-    return _variantField_16 ??= 0;
-  }
-
-  /// The minor component of the actual language version (not just override).
-  set compilationUnit_languageVersionMinor(int value) {
+  /// The language version information.
+  set compilationUnit_languageVersion(
+      LinkedLibraryLanguageVersionBuilder value) {
     assert(kind == idl.LinkedNodeKind.compilationUnit);
-    assert(value == null || value >= 0);
-    _variantField_16 = value;
-  }
-
-  set integerLiteral_value(int value) {
-    assert(kind == idl.LinkedNodeKind.integerLiteral);
-    assert(value == null || value >= 0);
-    _variantField_16 = value;
+    _variantField_40 = value;
   }
 
   @override
@@ -7150,6 +7351,18 @@
   }
 
   @override
+  int get integerLiteral_value {
+    assert(kind == idl.LinkedNodeKind.integerLiteral);
+    return _variantField_16 ??= 0;
+  }
+
+  set integerLiteral_value(int value) {
+    assert(kind == idl.LinkedNodeKind.integerLiteral);
+    assert(value == null || value >= 0);
+    _variantField_16 = value;
+  }
+
+  @override
   String get interpolationString_value {
     assert(kind == idl.LinkedNodeKind.interpolationString);
     return _variantField_30 ??= '';
@@ -7533,16 +7746,14 @@
   LinkedNodeBuilder.compilationUnit({
     List<LinkedNodeBuilder> compilationUnit_declarations,
     LinkedNodeBuilder compilationUnit_scriptTag,
-    int compilationUnit_languageVersionMajor,
     List<LinkedNodeBuilder> compilationUnit_directives,
-    int compilationUnit_languageVersionMinor,
+    LinkedLibraryLanguageVersionBuilder compilationUnit_languageVersion,
     int informativeId,
   })  : _kind = idl.LinkedNodeKind.compilationUnit,
         _variantField_2 = compilationUnit_declarations,
         _variantField_6 = compilationUnit_scriptTag,
-        _variantField_15 = compilationUnit_languageVersionMajor,
         _variantField_3 = compilationUnit_directives,
-        _variantField_16 = compilationUnit_languageVersionMinor,
+        _variantField_40 = compilationUnit_languageVersion,
         _variantField_36 = informativeId;
 
   LinkedNodeBuilder.conditionalExpression({
@@ -7991,11 +8202,11 @@
         _variantField_25 = expression_type;
 
   LinkedNodeBuilder.integerLiteral({
-    int integerLiteral_value,
     LinkedNodeTypeBuilder expression_type,
+    int integerLiteral_value,
   })  : _kind = idl.LinkedNodeKind.integerLiteral,
-        _variantField_16 = integerLiteral_value,
-        _variantField_25 = expression_type;
+        _variantField_25 = expression_type,
+        _variantField_16 = integerLiteral_value;
 
   LinkedNodeBuilder.interpolationExpression({
     LinkedNodeBuilder interpolationExpression_expression,
@@ -8529,6 +8740,7 @@
       compilationUnit_declarations?.forEach((b) => b.flushInformative());
       compilationUnit_scriptTag?.flushInformative();
       compilationUnit_directives?.forEach((b) => b.flushInformative());
+      compilationUnit_languageVersion?.flushInformative();
       informativeId = null;
     } else if (kind == idl.LinkedNodeKind.conditionalExpression) {
       conditionalExpression_condition?.flushInformative();
@@ -9162,10 +9374,10 @@
       }
       signature.addBool(this.compilationUnit_scriptTag != null);
       this.compilationUnit_scriptTag?.collectApiSignature(signature);
-      signature.addInt(this.compilationUnit_languageVersionMajor ?? 0);
-      signature.addInt(this.compilationUnit_languageVersionMinor ?? 0);
       signature.addInt(this.flags ?? 0);
       signature.addString(this.name ?? '');
+      signature.addBool(this.compilationUnit_languageVersion != null);
+      this.compilationUnit_languageVersion?.collectApiSignature(signature);
     } else if (kind == idl.LinkedNodeKind.conditionalExpression) {
       signature.addInt(this.kind == null ? 0 : this.kind.index);
       signature.addBool(this.conditionalExpression_condition != null);
@@ -10541,6 +10753,7 @@
     fb.Offset offset_variantField_13;
     fb.Offset offset_variantField_33;
     fb.Offset offset_variantField_3;
+    fb.Offset offset_variantField_40;
     fb.Offset offset_variantField_10;
     fb.Offset offset_variantField_25;
     fb.Offset offset_variantField_20;
@@ -10598,6 +10811,9 @@
       offset_variantField_3 = fbBuilder
           .writeList(_variantField_3.map((b) => b.finish(fbBuilder)).toList());
     }
+    if (_variantField_40 != null) {
+      offset_variantField_40 = _variantField_40.finish(fbBuilder);
+    }
     if (_variantField_10 != null) {
       offset_variantField_10 = _variantField_10.finish(fbBuilder);
     }
@@ -10696,8 +10912,8 @@
     if (offset_variantField_3 != null) {
       fbBuilder.addOffset(3, offset_variantField_3);
     }
-    if (_variantField_16 != null && _variantField_16 != 0) {
-      fbBuilder.addUint32(16, _variantField_16);
+    if (offset_variantField_40 != null) {
+      fbBuilder.addOffset(40, offset_variantField_40);
     }
     if (offset_variantField_10 != null) {
       fbBuilder.addOffset(10, offset_variantField_10);
@@ -10728,6 +10944,9 @@
     if (_variantField_36 != null && _variantField_36 != 0) {
       fbBuilder.addUint32(36, _variantField_36);
     }
+    if (_variantField_16 != null && _variantField_16 != 0) {
+      fbBuilder.addUint32(16, _variantField_16);
+    }
     if (offset_variantField_30 != null) {
       fbBuilder.addOffset(30, offset_variantField_30);
     }
@@ -10803,7 +11022,7 @@
   List<String> _variantField_33;
   idl.LinkedNodeCommentType _variantField_29;
   List<idl.LinkedNode> _variantField_3;
-  int _variantField_16;
+  idl.LinkedLibraryLanguageVersion _variantField_40;
   idl.LinkedNode _variantField_10;
   idl.LinkedNodeFormalParameterKind _variantField_26;
   double _variantField_21;
@@ -10813,6 +11032,7 @@
   int _flags;
   String _variantField_1;
   int _variantField_36;
+  int _variantField_16;
   String _variantField_30;
   idl.LinkedNode _variantField_14;
   idl.LinkedNodeKind _kind;
@@ -12378,14 +12598,6 @@
   }
 
   @override
-  int get compilationUnit_languageVersionMajor {
-    assert(kind == idl.LinkedNodeKind.compilationUnit);
-    _variantField_15 ??=
-        const fb.Uint32Reader().vTableGet(_bc, _bcOffset, 15, 0);
-    return _variantField_15;
-  }
-
-  @override
   int get constructorName_element {
     assert(kind == idl.LinkedNodeKind.constructorName);
     _variantField_15 ??=
@@ -12701,19 +12913,11 @@
   }
 
   @override
-  int get compilationUnit_languageVersionMinor {
+  idl.LinkedLibraryLanguageVersion get compilationUnit_languageVersion {
     assert(kind == idl.LinkedNodeKind.compilationUnit);
-    _variantField_16 ??=
-        const fb.Uint32Reader().vTableGet(_bc, _bcOffset, 16, 0);
-    return _variantField_16;
-  }
-
-  @override
-  int get integerLiteral_value {
-    assert(kind == idl.LinkedNodeKind.integerLiteral);
-    _variantField_16 ??=
-        const fb.Uint32Reader().vTableGet(_bc, _bcOffset, 16, 0);
-    return _variantField_16;
+    _variantField_40 ??= const _LinkedLibraryLanguageVersionReader()
+        .vTableGet(_bc, _bcOffset, 40, null);
+    return _variantField_40;
   }
 
   @override
@@ -12868,6 +13072,14 @@
   }
 
   @override
+  int get integerLiteral_value {
+    assert(kind == idl.LinkedNodeKind.integerLiteral);
+    _variantField_16 ??=
+        const fb.Uint32Reader().vTableGet(_bc, _bcOffset, 16, 0);
+    return _variantField_16;
+  }
+
+  @override
   String get interpolationString_value {
     assert(kind == idl.LinkedNodeKind.interpolationString);
     _variantField_30 ??=
@@ -13296,18 +13508,14 @@
         _result["compilationUnit_scriptTag"] =
             compilationUnit_scriptTag.toJson();
       }
-      if (compilationUnit_languageVersionMajor != 0) {
-        _result["compilationUnit_languageVersionMajor"] =
-            compilationUnit_languageVersionMajor;
-      }
       if (compilationUnit_directives.isNotEmpty) {
         _result["compilationUnit_directives"] = compilationUnit_directives
             .map((_value) => _value.toJson())
             .toList();
       }
-      if (compilationUnit_languageVersionMinor != 0) {
-        _result["compilationUnit_languageVersionMinor"] =
-            compilationUnit_languageVersionMinor;
+      if (compilationUnit_languageVersion != null) {
+        _result["compilationUnit_languageVersion"] =
+            compilationUnit_languageVersion.toJson();
       }
       if (informativeId != 0) {
         _result["informativeId"] = informativeId;
@@ -13980,12 +14188,12 @@
       }
     }
     if (kind == idl.LinkedNodeKind.integerLiteral) {
-      if (integerLiteral_value != 0) {
-        _result["integerLiteral_value"] = integerLiteral_value;
-      }
       if (expression_type != null) {
         _result["expression_type"] = expression_type.toJson();
       }
+      if (integerLiteral_value != 0) {
+        _result["integerLiteral_value"] = integerLiteral_value;
+      }
     }
     if (kind == idl.LinkedNodeKind.interpolationExpression) {
       if (interpolationExpression_expression != null) {
@@ -14839,11 +15047,8 @@
       return {
         "compilationUnit_declarations": compilationUnit_declarations,
         "compilationUnit_scriptTag": compilationUnit_scriptTag,
-        "compilationUnit_languageVersionMajor":
-            compilationUnit_languageVersionMajor,
         "compilationUnit_directives": compilationUnit_directives,
-        "compilationUnit_languageVersionMinor":
-            compilationUnit_languageVersionMinor,
+        "compilationUnit_languageVersion": compilationUnit_languageVersion,
         "flags": flags,
         "informativeId": informativeId,
         "kind": kind,
@@ -15351,9 +15556,9 @@
     }
     if (kind == idl.LinkedNodeKind.integerLiteral) {
       return {
-        "integerLiteral_value": integerLiteral_value,
         "expression_type": expression_type,
         "flags": flags,
+        "integerLiteral_value": integerLiteral_value,
         "kind": kind,
         "name": name,
       };
diff --git a/pkg/analyzer/lib/src/summary/format.fbs b/pkg/analyzer/lib/src/summary/format.fbs
index e797d29..3829aa2 100644
--- a/pkg/analyzer/lib/src/summary/format.fbs
+++ b/pkg/analyzer/lib/src/summary/format.fbs
@@ -1063,6 +1063,18 @@
   templateValues:[string] (id: 1);
 }
 
+table LinkedLanguageVersion {
+  major:uint (id: 0);
+
+  minor:uint (id: 1);
+}
+
+table LinkedLibraryLanguageVersion {
+  override2:LinkedLanguageVersion (id: 1);
+
+  package:LinkedLanguageVersion (id: 0);
+}
+
 /// Information about a linked AST node.
 table LinkedNode {
   /// The explicit or inferred return type of a function typed node.
@@ -1102,8 +1114,8 @@
 
   variantField_3:[LinkedNode] (id: 3);
 
-  /// The minor component of the actual language version (not just override).
-  variantField_16:uint (id: 16);
+  /// The language version information.
+  variantField_40:LinkedLibraryLanguageVersion (id: 40);
 
   variantField_10:LinkedNode (id: 10);
 
@@ -1123,6 +1135,8 @@
 
   variantField_36:uint (id: 36);
 
+  variantField_16:uint (id: 16);
+
   variantField_30:string (id: 30);
 
   variantField_14:LinkedNode (id: 14);
diff --git a/pkg/analyzer/lib/src/summary/idl.dart b/pkg/analyzer/lib/src/summary/idl.dart
index da3a10a..5648d6e 100644
--- a/pkg/analyzer/lib/src/summary/idl.dart
+++ b/pkg/analyzer/lib/src/summary/idl.dart
@@ -635,6 +635,22 @@
   unit
 }
 
+abstract class LinkedLanguageVersion extends base.SummaryClass {
+  @Id(0)
+  int get major;
+
+  @Id(1)
+  int get minor;
+}
+
+abstract class LinkedLibraryLanguageVersion extends base.SummaryClass {
+  @Id(1)
+  LinkedLanguageVersion get override2;
+
+  @Id(0)
+  LinkedLanguageVersion get package;
+}
+
 /// Information about a linked AST node.
 @Variant('kind')
 abstract class LinkedNode extends base.SummaryClass {
@@ -848,13 +864,9 @@
   @VariantId(3, variant: LinkedNodeKind.compilationUnit)
   List<LinkedNode> get compilationUnit_directives;
 
-  /// The major component of the actual language version (not just override).
-  @VariantId(15, variant: LinkedNodeKind.compilationUnit)
-  int get compilationUnit_languageVersionMajor;
-
-  /// The minor component of the actual language version (not just override).
-  @VariantId(16, variant: LinkedNodeKind.compilationUnit)
-  int get compilationUnit_languageVersionMinor;
+  /// The language version information.
+  @VariantId(40, variant: LinkedNodeKind.compilationUnit)
+  LinkedLibraryLanguageVersion get compilationUnit_languageVersion;
 
   @VariantId(6, variant: LinkedNodeKind.compilationUnit)
   LinkedNode get compilationUnit_scriptTag;
diff --git a/pkg/analyzer/lib/src/summary/summary_file_builder.dart b/pkg/analyzer/lib/src/summary/summary_file_builder.dart
index 477500b..431ff50 100644
--- a/pkg/analyzer/lib/src/summary/summary_file_builder.dart
+++ b/pkg/analyzer/lib/src/summary/summary_file_builder.dart
@@ -6,9 +6,12 @@
 import 'package:analyzer/dart/analysis/features.dart';
 import 'package:analyzer/dart/analysis/utilities.dart';
 import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer/dart/element/element.dart';
 import 'package:analyzer/file_system/file_system.dart';
 import 'package:analyzer/file_system/physical_file_system.dart';
+import 'package:analyzer/src/dart/analysis/experiments.dart';
 import 'package:analyzer/src/dart/analysis/session.dart';
+import 'package:analyzer/src/dart/ast/ast.dart';
 import 'package:analyzer/src/dart/sdk/sdk.dart';
 import 'package:analyzer/src/generated/engine.dart';
 import 'package:analyzer/src/generated/source.dart';
@@ -240,6 +243,12 @@
       );
     }
 
+    var unit = result.unit as CompilationUnitImpl;
+    unit.languageVersion = LibraryLanguageVersion(
+      package: ExperimentStatus.currentVersion,
+      override: null,
+    );
+
     return result.unit;
   }
 
diff --git a/pkg/analyzer/lib/src/summary2/ast_binary_writer.dart b/pkg/analyzer/lib/src/summary2/ast_binary_writer.dart
index bbdd1d7..f8bdeee 100644
--- a/pkg/analyzer/lib/src/summary2/ast_binary_writer.dart
+++ b/pkg/analyzer/lib/src/summary2/ast_binary_writer.dart
@@ -286,8 +286,18 @@
     var builder = LinkedNodeBuilder.compilationUnit(
       compilationUnit_declarations: _writeNodeList(node.declarations),
       compilationUnit_directives: _writeNodeList(node.directives),
-      compilationUnit_languageVersionMajor: nodeImpl.languageVersionMajor,
-      compilationUnit_languageVersionMinor: nodeImpl.languageVersionMinor,
+      compilationUnit_languageVersion: LinkedLibraryLanguageVersionBuilder(
+        package: LinkedLanguageVersionBuilder(
+          major: nodeImpl.languageVersion.package.major,
+          minor: nodeImpl.languageVersion.package.minor,
+        ),
+        override2: nodeImpl.languageVersion.override != null
+            ? LinkedLanguageVersionBuilder(
+                major: nodeImpl.languageVersion.override.major,
+                minor: nodeImpl.languageVersion.override.minor,
+              )
+            : null,
+      ),
       compilationUnit_scriptTag: node.scriptTag?.accept(this),
       informativeId: getInformativeId(node),
     );
diff --git a/pkg/analyzer/lib/src/summary2/lazy_ast.dart b/pkg/analyzer/lib/src/summary2/lazy_ast.dart
index 7b39a78..43008b7 100644
--- a/pkg/analyzer/lib/src/summary2/lazy_ast.dart
+++ b/pkg/analyzer/lib/src/summary2/lazy_ast.dart
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 
 import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer/dart/element/element.dart';
 import 'package:analyzer/dart/element/type.dart';
 import 'package:analyzer/src/dart/ast/ast.dart';
 import 'package:analyzer/src/summary/format.dart';
@@ -10,6 +11,7 @@
 import 'package:analyzer/src/summary2/ast_binary_flags.dart';
 import 'package:analyzer/src/summary2/ast_binary_reader.dart';
 import 'package:analyzer/src/summary2/linked_unit_context.dart';
+import 'package:pub_semver/pub_semver.dart';
 
 /// Accessor for reading AST lazily, or read data that is stored in IDL, but
 /// cannot be stored in AST, like inferred types.
@@ -388,20 +390,19 @@
     return node.getProperty(_key);
   }
 
-  static int getLanguageVersionMajor(CompilationUnit node) {
+  static LibraryLanguageVersion getLanguageVersion(CompilationUnit node) {
     var lazy = get(node);
     if (lazy != null) {
-      return lazy.data.compilationUnit_languageVersionMajor;
+      var package = lazy.data.compilationUnit_languageVersion.package;
+      var override = lazy.data.compilationUnit_languageVersion.override2;
+      return LibraryLanguageVersion(
+        package: Version(package.major, package.minor, 0),
+        override: override != null
+            ? Version(override.major, override.minor, 0)
+            : null,
+      );
     }
-    return node.languageVersionToken.major;
-  }
-
-  static int getLanguageVersionMinor(CompilationUnit node) {
-    var lazy = get(node);
-    if (lazy != null) {
-      return lazy.data.compilationUnit_languageVersionMinor;
-    }
-    return node.languageVersionToken.minor;
+    return (node as CompilationUnitImpl).languageVersion;
   }
 }
 
diff --git a/pkg/analyzer/lib/src/summary2/linked_unit_context.dart b/pkg/analyzer/lib/src/summary2/linked_unit_context.dart
index 21beafa..a5d02ce 100644
--- a/pkg/analyzer/lib/src/summary2/linked_unit_context.dart
+++ b/pkg/analyzer/lib/src/summary2/linked_unit_context.dart
@@ -446,12 +446,8 @@
     }
   }
 
-  int getLanguageVersionMajor(CompilationUnit node) {
-    return LazyCompilationUnit.getLanguageVersionMajor(node);
-  }
-
-  int getLanguageVersionMinor(CompilationUnit node) {
-    return LazyCompilationUnit.getLanguageVersionMinor(node);
+  LibraryLanguageVersion getLanguageVersion(CompilationUnit node) {
+    return LazyCompilationUnit.getLanguageVersion(node);
   }
 
   Comment getLibraryDocumentationComment(CompilationUnit unit) {
diff --git a/pkg/analyzer/test/src/dart/resolution/library_element_test.dart b/pkg/analyzer/test/src/dart/resolution/library_element_test.dart
index 49f98f6..15bef67 100644
--- a/pkg/analyzer/test/src/dart/resolution/library_element_test.dart
+++ b/pkg/analyzer/test/src/dart/resolution/library_element_test.dart
@@ -4,6 +4,8 @@
 
 import 'package:analyzer/src/context/packages.dart';
 import 'package:analyzer/src/dart/analysis/experiments.dart';
+import 'package:meta/meta.dart';
+import 'package:pub_semver/pub_semver.dart';
 import 'package:test/test.dart';
 import 'package:test_reflective_loader/test_reflective_loader.dart';
 
@@ -82,40 +84,60 @@
 ''');
 
     // No override.
-    await _assertLanguageVersion('package:test/a.dart', 2, 7);
+    await _assertLanguageVersion(
+      uriStr: 'package:test/a.dart',
+      package: Version.parse('2.7.0'),
+      override: null,
+    );
 
     // Valid override, less than the latest supported language version.
-    await _assertLanguageVersion('package:test/b.dart', 2, 6);
+    await _assertLanguageVersion(
+      uriStr: 'package:test/b.dart',
+      package: Version.parse('2.7.0'),
+      override: Version.parse('2.6.0'),
+    );
 
     // Valid override, even if greater than the package language version.
-    await _assertLanguageVersion('package:test/c.dart', 2, 9);
+    await _assertLanguageVersion(
+      uriStr: 'package:test/c.dart',
+      package: Version.parse('2.7.0'),
+      override: Version.parse('2.9.0'),
+    );
 
     // Invalid override: minor is greater than the latest minor.
-    await _assertLanguageVersion('package:test/d.dart', 2, 7);
+    await _assertLanguageVersion(
+      uriStr: 'package:test/d.dart',
+      package: Version.parse('2.7.0'),
+      override: null,
+    );
 
     // Invalid override: major is greater than the latest major.
-    await _assertLanguageVersion('package:test/e.dart', 2, 7);
+    await _assertLanguageVersion(
+      uriStr: 'package:test/e.dart',
+      package: Version.parse('2.7.0'),
+      override: null,
+    );
 
     await _assertLanguageVersionCurrent('package:aaa/a.dart');
     await _assertLanguageVersionCurrent('package:aaa/b.dart');
     await _assertLanguageVersionCurrent('package:aaa/c.dart');
   }
 
-  Future<void> _assertLanguageVersion(
-    String uriStr,
-    int major,
-    int minor,
-  ) async {
+  Future<void> _assertLanguageVersion({
+    @required String uriStr,
+    @required Version package,
+    @required Version override,
+  }) async {
     var element = await driver.getLibraryByUri(uriStr);
-    expect(element.languageVersionMajor, major);
-    expect(element.languageVersionMinor, minor);
+    expect(element.languageVersion.package, package);
+    expect(element.languageVersion.override, override);
   }
 
   Future<void> _assertLanguageVersionCurrent(String uriStr) async {
     await _assertLanguageVersion(
-      uriStr,
-      ExperimentStatus.currentVersion.major,
-      ExperimentStatus.currentVersion.minor,
+      uriStr: uriStr,
+      package: ExperimentStatus.currentVersion,
+      override: null,
     );
   }
 }
diff --git a/pkg/analyzer/test/src/diagnostics/invalid_language_override_greater_test.dart b/pkg/analyzer/test/src/diagnostics/invalid_language_override_greater_test.dart
index 9c62cae..301431e 100644
--- a/pkg/analyzer/test/src/diagnostics/invalid_language_override_greater_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/invalid_language_override_greater_test.dart
@@ -32,8 +32,8 @@
       error(HintCode.INVALID_LANGUAGE_VERSION_OVERRIDE_GREATER, 0, 15),
     ]);
     _assertUnitLanguageVersion(
-      major: latestVersion.major,
-      minor: latestVersion.minor,
+      package: latestVersion,
+      override: null,
     );
   }
 
@@ -43,7 +43,10 @@
 // @dart = 2.9
 int? a;
 ''');
-    _assertUnitLanguageVersion(major: 2, minor: 9);
+    _assertUnitLanguageVersion(
+      package: Version.parse('2.5.0'),
+      override: Version.parse('2.9.0'),
+    );
   }
 
   test_lessThanPackage() async {
@@ -52,16 +55,20 @@
 // @dart = 2.4
 class A {}
 ''');
-    _assertUnitLanguageVersion(major: 2, minor: 4);
+    _assertUnitLanguageVersion(
+      package: Version.parse('2.5.0'),
+      override: Version.parse('2.4.0'),
+    );
   }
 
   void _assertUnitLanguageVersion({
-    @required int major,
-    @required int minor,
+    @required Version package,
+    @required Version override,
   }) {
     var unitImpl = result.unit as CompilationUnitImpl;
-    expect(unitImpl.languageVersionMajor, major);
-    expect(unitImpl.languageVersionMinor, minor);
+    var languageVersion = unitImpl.languageVersion;
+    expect(languageVersion.package, package);
+    expect(languageVersion.override, override);
   }
 
   void _configureTestPackageLanguageVersion(String versionStr) {
diff --git a/pkg/analyzer/test/src/summary/test_strategies.dart b/pkg/analyzer/test/src/summary/test_strategies.dart
index ef149a1..8ec7cc3 100644
--- a/pkg/analyzer/test/src/summary/test_strategies.dart
+++ b/pkg/analyzer/test/src/summary/test_strategies.dart
@@ -6,8 +6,11 @@
 import 'package:analyzer/dart/analysis/features.dart';
 import 'package:analyzer/dart/ast/ast.dart';
 import 'package:analyzer/dart/ast/token.dart';
+import 'package:analyzer/dart/element/element.dart';
 import 'package:analyzer/error/listener.dart';
 import 'package:analyzer/file_system/memory_file_system.dart';
+import 'package:analyzer/src/dart/analysis/experiments.dart';
+import 'package:analyzer/src/dart/ast/ast.dart';
 import 'package:analyzer/src/dart/scanner/reader.dart';
 import 'package:analyzer/src/dart/scanner/scanner.dart';
 import 'package:analyzer/src/generated/parser.dart';
@@ -38,6 +41,13 @@
   );
   CompilationUnit unit = parser.parseCompilationUnit(token);
   unit.lineInfo = LineInfo(scanner.lineStarts);
+
+  var unitImpl = unit as CompilationUnitImpl;
+  unitImpl.languageVersion = LibraryLanguageVersion(
+    package: ExperimentStatus.currentVersion,
+    override: null,
+  );
+
   return unit;
 }