Skip to content

Commit 021a49e

Browse files
committed
Update the way to get feature set and language version.
#42274 #42321 Change-Id: I5551ecd2803f9d26e720e5cf5671b3be8236bce9 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/151423 Reviewed-by: Brian Wilkerson <[email protected]>
1 parent d163850 commit 021a49e

File tree

10 files changed

+323
-75
lines changed

10 files changed

+323
-75
lines changed

pkg/analyzer/lib/error/error.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -390,6 +390,7 @@ const List<ErrorCode> errorCodeValues = [
390390
HintCode.INVALID_IMMUTABLE_ANNOTATION,
391391
HintCode.INVALID_LANGUAGE_VERSION_OVERRIDE_AT_SIGN,
392392
HintCode.INVALID_LANGUAGE_VERSION_OVERRIDE_EQUALS,
393+
HintCode.INVALID_LANGUAGE_VERSION_OVERRIDE_GREATER,
393394
HintCode.INVALID_LANGUAGE_VERSION_OVERRIDE_LOCATION,
394395
HintCode.INVALID_LANGUAGE_VERSION_OVERRIDE_LOWER_CASE,
395396
HintCode.INVALID_LANGUAGE_VERSION_OVERRIDE_NUMBER,

pkg/analyzer/lib/src/dart/analysis/feature_set_provider.dart

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ class FeatureSetProvider {
3131
_packageDefaultFeatureSet = packageDefaultFeatureSet,
3232
_nonPackageDefaultFeatureSet = nonPackageDefaultFeatureSet;
3333

34-
/// Return the [FeatureSet] for the Dart file with the given [uri].
34+
/// Return the [FeatureSet] for the package that contains the file.
3535
FeatureSet getFeatureSet(String path, Uri uri) {
3636
if (uri.isScheme('dart')) {
3737
var pathSegments = uri.pathSegments;
@@ -57,15 +57,16 @@ class FeatureSetProvider {
5757

5858
var package = _packages.packageForPath(path);
5959
if (package != null) {
60-
var languageVersion = package.languageVersion;
61-
languageVersion ??= ExperimentStatus.currentVersion;
62-
return _packageDefaultFeatureSet.restrictToVersion(languageVersion);
60+
return _packageDefaultFeatureSet;
6361
}
6462

6563
return _nonPackageDefaultFeatureSet;
6664
}
6765

68-
/// Return the language version configured for the file.
66+
/// Return the language version for the package that contains the file.
67+
///
68+
/// Each individual file might use `// @dart` to override this version, to
69+
/// be either lower, or higher than the package language version.
6970
Version getLanguageVersion(String path, Uri uri) {
7071
var package = _packages.packageForPath(path);
7172
if (package != null) {

pkg/analyzer/lib/src/dart/analysis/file_state.dart

Lines changed: 45 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,12 @@ import 'package:analyzer/dart/analysis/features.dart';
1212
import 'package:analyzer/dart/ast/ast.dart';
1313
import 'package:analyzer/dart/ast/standard_ast_factory.dart';
1414
import 'package:analyzer/dart/ast/token.dart';
15+
import 'package:analyzer/error/error.dart';
1516
import 'package:analyzer/error/listener.dart';
1617
import 'package:analyzer/file_system/file_system.dart';
1718
import 'package:analyzer/src/dart/analysis/byte_store.dart';
1819
import 'package:analyzer/src/dart/analysis/defined_names.dart';
20+
import 'package:analyzer/src/dart/analysis/experiments.dart';
1921
import 'package:analyzer/src/dart/analysis/feature_set_provider.dart';
2022
import 'package:analyzer/src/dart/analysis/library_graph.dart';
2123
import 'package:analyzer/src/dart/analysis/performance_logger.dart';
@@ -24,6 +26,7 @@ import 'package:analyzer/src/dart/analysis/unlinked_api_signature.dart';
2426
import 'package:analyzer/src/dart/ast/ast.dart';
2527
import 'package:analyzer/src/dart/scanner/reader.dart';
2628
import 'package:analyzer/src/dart/scanner/scanner.dart';
29+
import 'package:analyzer/src/error/codes.dart';
2730
import 'package:analyzer/src/generated/engine.dart';
2831
import 'package:analyzer/src/generated/parser.dart';
2932
import 'package:analyzer/src/generated/source.dart';
@@ -37,6 +40,7 @@ import 'package:analyzer/src/summary2/informative_data.dart';
3740
import 'package:convert/convert.dart';
3841
import 'package:crypto/crypto.dart';
3942
import 'package:meta/meta.dart';
43+
import 'package:pub_semver/pub_semver.dart';
4044

4145
var counterFileStateRefresh = 0;
4246
var counterUnlinkedLinkedBytes = 0;
@@ -567,6 +571,36 @@ class FileState {
567571
return _fsState.getFileForUri(absoluteUri);
568572
}
569573

574+
/// Return the language version specified in the [versionToken], or the
575+
/// language version from the [FeatureSetProvider].
576+
Version _getLanguageVersion(
577+
LanguageVersionToken versionToken,
578+
AnalysisErrorListener errorListener,
579+
) {
580+
if (versionToken != null) {
581+
var latestVersion = ExperimentStatus.currentVersion;
582+
if (versionToken.major > latestVersion.major ||
583+
versionToken.major == latestVersion.major &&
584+
versionToken.minor > latestVersion.minor) {
585+
errorListener.onError(
586+
AnalysisError(
587+
source,
588+
versionToken.offset,
589+
versionToken.length,
590+
HintCode.INVALID_LANGUAGE_VERSION_OVERRIDE_GREATER,
591+
[latestVersion.major, latestVersion.minor],
592+
),
593+
);
594+
// Fall-through, use the package language version.
595+
} else {
596+
return Version(versionToken.major, versionToken.minor, 0);
597+
}
598+
}
599+
600+
var featureSetProvider = _fsState.featureSetProvider;
601+
return featureSetProvider.getLanguageVersion(path, uri);
602+
}
603+
570604
/**
571605
* Invalidate any data that depends on the current unlinked data of the file,
572606
* because [refresh] is going to recompute the unlinked data.
@@ -599,14 +633,17 @@ class FileState {
599633
});
600634
LineInfo lineInfo = LineInfo(scanner.lineStarts);
601635

636+
var languageVersion = _getLanguageVersion(
637+
scanner.languageVersion,
638+
errorListener,
639+
);
640+
var featureSet = _featureSet.restrictToVersion(languageVersion);
641+
602642
bool useFasta = analysisOptions.useFastaParser;
603-
// Pass the feature set from the scanner to the parser
604-
// because the scanner may have detected a language version comment
605-
// and downgraded the feature set it holds.
606643
Parser parser = Parser(
607644
source,
608645
errorListener,
609-
featureSet: scanner.featureSet,
646+
featureSet: featureSet,
610647
useFasta: useFasta,
611648
);
612649
parser.enableOptionalNewAndConst = true;
@@ -616,7 +653,10 @@ class FileState {
616653
try {
617654
unit = parser.parseCompilationUnit(token);
618655
unit.lineInfo = lineInfo;
619-
_setLanguageVersion(unit, scanner.languageVersion);
656+
657+
var unitImpl = unit as CompilationUnitImpl;
658+
unitImpl.languageVersionMajor = languageVersion.major;
659+
unitImpl.languageVersionMinor = languageVersion.minor;
620660
} catch (e) {
621661
throw StateError('''
622662
Parser error.
@@ -647,22 +687,6 @@ $content
647687
return directive.uri;
648688
}
649689

650-
/// Set the language version into the [unit], from the [languageVersionToken]
651-
/// override, or the configured from the [FeatureSetProvider].
652-
void _setLanguageVersion(
653-
CompilationUnitImpl unit,
654-
LanguageVersionToken languageVersionToken,
655-
) {
656-
if (languageVersionToken != null) {
657-
unit.languageVersionMajor = languageVersionToken.major;
658-
unit.languageVersionMinor = languageVersionToken.minor;
659-
} else {
660-
var version = _fsState.featureSetProvider.getLanguageVersion(path, uri);
661-
unit.languageVersionMajor = version.major;
662-
unit.languageVersionMinor = version.minor;
663-
}
664-
}
665-
666690
static UnlinkedUnit2Builder serializeAstUnlinked2(CompilationUnit unit) {
667691
var exports = <UnlinkedNamespaceDirectiveBuilder>[];
668692
var imports = <UnlinkedNamespaceDirectiveBuilder>[];

pkg/analyzer/lib/src/dart/error/hint_codes.dart

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -742,6 +742,15 @@ class HintCode extends AnalyzerErrorCode {
742742
correction: "Specify a Dart language version override with a comment "
743743
"like '// @dart = 2.0'.");
744744

745+
static const HintCode INVALID_LANGUAGE_VERSION_OVERRIDE_GREATER =
746+
HintCodeWithUniqueName(
747+
'INVALID_LANGUAGE_VERSION_OVERRIDE',
748+
'INVALID_LANGUAGE_VERSION_OVERRIDE_GREATER',
749+
"The language version override can't specify a version greater than the "
750+
"latest known language version: {0}.{1}",
751+
correction: "Try removing the language version override.",
752+
);
753+
745754
static const HintCode INVALID_LANGUAGE_VERSION_OVERRIDE_LOCATION =
746755
HintCodeWithUniqueName(
747756
'INVALID_LANGUAGE_VERSION_OVERRIDE',

pkg/analyzer/test/src/dart/analysis/feature_set_provider_test.dart

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -196,18 +196,6 @@ class FeatureSetProviderTest with ResourceProviderMixin {
196196
libFolder: newFolder('/packages/aaa/lib'),
197197
languageVersion: null,
198198
),
199-
'bbb': Package(
200-
name: 'bbb',
201-
rootFolder: newFolder('/packages/bbb'),
202-
libFolder: newFolder('/packages/bbb/lib'),
203-
languageVersion: Version(2, 7, 0),
204-
),
205-
'ccc': Package(
206-
name: 'ccc',
207-
rootFolder: newFolder('/packages/ccc'),
208-
libFolder: newFolder('/packages/ccc/lib'),
209-
languageVersion: Version(2, 9, 0),
210-
),
211199
},
212200
);
213201

@@ -226,14 +214,6 @@ class FeatureSetProviderTest with ResourceProviderMixin {
226214
_assertNonNullableForPath('/packages/aaa/lib/b.dart', true);
227215
_assertNonNullableForPath('/packages/aaa/test/c.dart', true);
228216

229-
_assertNonNullableForPath('/packages/bbb/a.dart', false);
230-
_assertNonNullableForPath('/packages/bbb/lib/b.dart', false);
231-
_assertNonNullableForPath('/packages/bbb/test/c.dart', false);
232-
233-
_assertNonNullableForPath('/packages/ccc/a.dart', true);
234-
_assertNonNullableForPath('/packages/ccc/lib/b.dart', true);
235-
_assertNonNullableForPath('/packages/ccc/test/c.dart', true);
236-
237217
_assertNonNullableForPath('/other/file.dart', false);
238218
}
239219

pkg/analyzer/test/src/dart/resolution/language_version_test.dart

Lines changed: 117 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,20 @@ import 'package:analyzer/dart/analysis/features.dart';
66
import 'package:analyzer/src/context/packages.dart';
77
import 'package:analyzer/src/dart/analysis/experiments.dart';
88
import 'package:analyzer/src/generated/engine.dart';
9+
import 'package:analyzer/src/test_utilities/mock_sdk.dart';
910
import 'package:test_reflective_loader/test_reflective_loader.dart';
1011

1112
import 'driver_resolution.dart';
1213

1314
main() {
1415
defineReflectiveSuite(() {
15-
defineReflectiveTests(DefaultNonNullableTest);
16+
defineReflectiveTests(NullSafetyUsingAllowedExperimentsTest);
17+
defineReflectiveTests(NullSafetyExperimentGlobalTest);
1618
});
1719
}
1820

1921
@reflectiveTest
20-
class DefaultNonNullableTest extends DriverResolutionTest {
22+
class NullSafetyExperimentGlobalTest extends _FeaturesTest {
2123
@override
2224
AnalysisOptionsImpl get analysisOptions => AnalysisOptionsImpl()
2325
..contextFeatures = FeatureSet.fromEnableFlags(
@@ -46,12 +48,7 @@ class DefaultNonNullableTest extends DriverResolutionTest {
4648
]
4749
}
4850
''');
49-
driver.configure(
50-
packages: findPackagesFrom(
51-
resourceProvider,
52-
getFolder('/test'),
53-
),
54-
);
51+
_configureTestWithJsonConfig();
5552

5653
newFile('/aaa/lib/a.dart', content: r'''
5754
int a = 0;
@@ -89,12 +86,7 @@ var z = PI;
8986
]
9087
}
9188
''');
92-
driver.configure(
93-
packages: findPackagesFrom(
94-
resourceProvider,
95-
getFolder('/test'),
96-
),
97-
);
89+
_configureTestWithJsonConfig();
9890

9991
newFile('/aaa/lib/a.dart', content: r'''
10092
int a = 0;
@@ -113,3 +105,114 @@ var z = PI;
113105
assertType(findElement.topVar('z').type, 'double');
114106
}
115107
}
108+
109+
@reflectiveTest
110+
class NullSafetyUsingAllowedExperimentsTest extends _FeaturesTest {
111+
@override
112+
bool get typeToStringWithNullability => true;
113+
114+
test_jsonConfig_disable() async {
115+
_configureAllowedExperimentsTestNullSafety();
116+
117+
newFile('/test/.dart_tool/package_config.json', content: '''
118+
{
119+
"configVersion": 2,
120+
"packages": [
121+
{
122+
"name": "test",
123+
"rootUri": "../",
124+
"packageUri": "lib/",
125+
"languageVersion": "2.8"
126+
}
127+
]
128+
}
129+
''');
130+
_configureTestWithJsonConfig();
131+
132+
await assertNoErrorsInCode('''
133+
var x = 0;
134+
''');
135+
assertType(findElement.topVar('x').type, 'int*');
136+
137+
// Upgrade the language version to `2.9`, so enabled Null Safety.
138+
_changeTestFile();
139+
await assertNoErrorsInCode('''
140+
// @dart = 2.9
141+
var x = 0;
142+
''');
143+
assertType(findElement.topVar('x').type, 'int');
144+
}
145+
146+
test_jsonConfig_enable() async {
147+
_configureAllowedExperimentsTestNullSafety();
148+
149+
newFile('/test/.dart_tool/package_config.json', content: '''
150+
{
151+
"configVersion": 2,
152+
"packages": [
153+
{
154+
"name": "test",
155+
"rootUri": "../",
156+
"packageUri": "lib/"
157+
}
158+
]
159+
}
160+
''');
161+
_configureTestWithJsonConfig();
162+
163+
await assertNoErrorsInCode('''
164+
var x = 0;
165+
''');
166+
assertType(findElement.topVar('x').type, 'int');
167+
168+
// Downgrade the version to `2.8`, so disable Null Safety.
169+
_changeTestFile();
170+
await assertNoErrorsInCode('''
171+
// @dart = 2.8
172+
var x = 0;
173+
''');
174+
assertType(findElement.topVar('x').type, 'int*');
175+
}
176+
177+
void _changeTestFile() {
178+
var path = convertPath('/test/lib/test.dart');
179+
driver.changeFile(path);
180+
}
181+
182+
void _configureAllowedExperimentsTestNullSafety() {
183+
_newSdkExperimentsFile(r'''
184+
{
185+
"version": 1,
186+
"experimentSets": {
187+
"nullSafety": ["non-nullable"]
188+
},
189+
"sdk": {
190+
"default": {
191+
"experimentSet": "nullSafety"
192+
}
193+
},
194+
"packages": {
195+
"test": {
196+
"experimentSet": "nullSafety"
197+
}
198+
}
199+
}
200+
''');
201+
}
202+
203+
void _newSdkExperimentsFile(String content) {
204+
newFile('$sdkRoot/lib/_internal/allowed_experiments.json',
205+
content: content);
206+
}
207+
}
208+
209+
class _FeaturesTest extends DriverResolutionTest {
210+
void _configureTestWithJsonConfig() {
211+
driver.configure(
212+
packages: findPackagesFrom(
213+
resourceProvider,
214+
getFolder('/test'),
215+
),
216+
);
217+
}
218+
}

0 commit comments

Comments
 (0)