Add the enable-experiment flag back to DAS

Bug: https://github.com/dart-lang/sdk/issues/48960
Change-Id: I6576f45e63e28902986db844df49a06a71385704
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/245202
Commit-Queue: Samuel Rawlins <srawlins@google.com>
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
This commit is contained in:
Sam Rawlins 2022-06-16 16:26:39 +00:00 committed by Commit Bot
parent 6f473f0249
commit 567b45fb1d
16 changed files with 194 additions and 58 deletions

View file

@ -874,6 +874,10 @@ class AnalysisServerOptions {
/// If set, this string will be reported as the protocol version.
String? reportProtocolVersion;
/// Experiments which have been enabled (or disabled) via the
/// `--enable-experiment` command-line option.
List<String> enabledExperiments = [];
}
class ServerContextManagerCallbacks extends ContextManagerCallbacks {

View file

@ -228,6 +228,7 @@ abstract class AbstractAnalysisServer {
resourceProvider,
sdkManager,
options.packagesFile,
options.enabledExperiments,
byteStore,
fileContentCache,
analysisPerformanceLogger,

View file

@ -7,6 +7,7 @@ import 'dart:collection';
import 'package:analysis_server/src/lsp/handlers/handlers.dart';
import 'package:analysis_server/src/services/correction/fix/data_driven/transform_set_parser.dart';
import 'package:analyzer/dart/analysis/features.dart';
import 'package:analyzer/error/listener.dart';
import 'package:analyzer/file_system/file_system.dart';
import 'package:analyzer/instrumentation/instrumentation.dart';
@ -206,6 +207,10 @@ class ContextManagerImpl implements ContextManager {
/// clean up when we destroy a context.
final bazelWatchedPathsPerFolder = <Folder, _BazelWatchedFiles>{};
/// Experiments which have been enabled (or disabled) via the
/// `--enable-experiment` command-line option.
final List<String> _enabledExperiments;
/// Information about the current/last queued context rebuild.
///
/// This is used when a new build is requested to cancel any in-progress
@ -216,6 +221,7 @@ class ContextManagerImpl implements ContextManager {
this.resourceProvider,
this.sdkManager,
this.packagesFile,
this._enabledExperiments,
this._byteStore,
this._fileContentCache,
this._performanceLog,
@ -456,6 +462,18 @@ class ContextManagerImpl implements ContextManager {
sdkPath: sdkManager.defaultSdkDirectory,
packagesFile: packagesFile,
fileContentCache: _fileContentCache,
updateAnalysisOptions2: ({
required analysisOptions,
required contextRoot,
required sdk,
}) {
if (_enabledExperiments.isNotEmpty) {
analysisOptions.contextFeatures = FeatureSet.fromEnableFlags2(
sdkLanguageVersion: sdk.languageVersion,
flags: _enabledExperiments,
);
}
},
);
for (var analysisContext in collection.contexts) {

View file

@ -65,6 +65,9 @@ class Driver implements ServerStarter {
static const String DISABLE_SERVER_FEATURE_SEARCH =
'disable-server-feature-search';
/// The name of the multi-option to enable one or more experiments.
static const String ENABLE_EXPERIMENT = 'enable-experiment';
/// The name of the option used to print usage information.
static const String HELP_OPTION = 'help';
@ -170,6 +173,11 @@ class Driver implements ServerStarter {
analysisServerOptions.reportProtocolVersion =
results[REPORT_PROTOCOL_VERSION] as String?;
analysisServerOptions.enabledExperiments =
results.wasParsed(ENABLE_EXPERIMENT)
? results[ENABLE_EXPERIMENT] as List<String>
: <String>[];
// Read in any per-SDK overrides specified in <sdk>/config/settings.json.
var sdkConfig = SdkConfiguration.readFromSdk();
analysisServerOptions.configurationOverrides = sdkConfig;
@ -689,6 +697,13 @@ class Driver implements ServerStarter {
help: 'The path to the package resolution configuration file, which '
'supplies a mapping of package names\ninto paths.',
);
parser.addMultiOption(
ENABLE_EXPERIMENT,
valueHelp: 'experiment',
help: 'Enable one or more experimental features '
'(see dart.dev/go/experiments).',
hide: true,
);
parser.addOption(
SERVER_PROTOCOL,
@ -784,8 +799,6 @@ class Driver implements ServerStarter {
parser.addFlag('dartpad', hide: true);
// Removed 11/15/2020.
parser.addFlag('enable-completion-model', hide: true);
// Removed 10/30/2020.
parser.addMultiOption('enable-experiment', hide: true);
// Removed 9/23/2020.
parser.addFlag('enable-instrumentation', hide: true);
// Removed 11/12/2020.

View file

@ -6,6 +6,7 @@ import 'package:_fe_analyzer_shared/src/macros/executor/multi_executor.dart'
as macro;
import 'package:analyzer/dart/analysis/analysis_context_collection.dart';
import 'package:analyzer/dart/analysis/context_locator.dart';
import 'package:analyzer/dart/analysis/context_root.dart';
import 'package:analyzer/dart/analysis/declared_variables.dart';
import 'package:analyzer/file_system/file_system.dart';
import 'package:analyzer/file_system/physical_file_system.dart';
@ -16,6 +17,7 @@ import 'package:analyzer/src/dart/analysis/driver_based_analysis_context.dart';
import 'package:analyzer/src/dart/analysis/file_content_cache.dart';
import 'package:analyzer/src/dart/analysis/performance_logger.dart';
import 'package:analyzer/src/generated/engine.dart' show AnalysisOptionsImpl;
import 'package:analyzer/src/generated/sdk.dart';
import 'package:analyzer/src/summary2/kernel_compilation_service.dart';
import 'package:analyzer/src/summary2/macro.dart';
import 'package:analyzer/src/util/sdk.dart';
@ -53,7 +55,15 @@ class AnalysisContextCollectionImpl implements AnalysisContextCollection {
String? sdkSummaryPath,
AnalysisDriverScheduler? scheduler,
FileContentCache? fileContentCache,
void Function(AnalysisOptionsImpl)? updateAnalysisOptions,
@Deprecated('Use updateAnalysisOptions2, which must be a function that '
'accepts a second parameter')
void Function(AnalysisOptionsImpl)? updateAnalysisOptions,
void Function({
required AnalysisOptionsImpl analysisOptions,
required ContextRoot contextRoot,
required DartSdk sdk,
})?
updateAnalysisOptions2,
}) : resourceProvider =
resourceProvider ?? PhysicalResourceProvider.INSTANCE {
sdkPath ??= getSdkPath();
@ -61,6 +71,12 @@ class AnalysisContextCollectionImpl implements AnalysisContextCollection {
_throwIfAnyNotAbsoluteNormalizedPath(includedPaths);
_throwIfNotAbsoluteNormalizedPath(sdkPath);
if (updateAnalysisOptions != null && updateAnalysisOptions2 != null) {
throw ArgumentError(
'Either updateAnalysisOptions or updateAnalysisOptions2 must be '
'given, but not both.');
}
var contextLocator = ContextLocator(
resourceProvider: this.resourceProvider,
);
@ -86,7 +102,9 @@ class AnalysisContextCollectionImpl implements AnalysisContextCollection {
sdkPath: sdkPath,
sdkSummaryPath: sdkSummaryPath,
scheduler: scheduler,
// ignore: deprecated_member_use_from_same_package
updateAnalysisOptions: updateAnalysisOptions,
updateAnalysisOptions2: updateAnalysisOptions2,
fileContentCache: fileContentCache,
macroKernelBuilder: macroKernelBuilder,
macroExecutor: macroExecutor,

View file

@ -59,7 +59,14 @@ class ContextBuilderImpl implements ContextBuilder {
AnalysisDriverScheduler? scheduler,
String? sdkPath,
String? sdkSummaryPath,
void Function(AnalysisOptionsImpl)? updateAnalysisOptions,
@Deprecated('Use updateAnalysisOptions2')
void Function(AnalysisOptionsImpl)? updateAnalysisOptions,
void Function({
required AnalysisOptionsImpl analysisOptions,
required ContextRoot contextRoot,
required DartSdk sdk,
})?
updateAnalysisOptions2,
FileContentCache? fileContentCache,
MacroKernelBuilder? macroKernelBuilder,
macro.MultiMacroExecutor? macroExecutor,
@ -67,6 +74,11 @@ class ContextBuilderImpl implements ContextBuilder {
// TODO(scheglov) Remove this, and make `sdkPath` required.
sdkPath ??= getSdkPath();
ArgumentError.checkNotNull(sdkPath, 'sdkPath');
if (updateAnalysisOptions != null && updateAnalysisOptions2 != null) {
throw ArgumentError(
'Either updateAnalysisOptions or updateAnalysisOptions2 must be '
'given, but not both.');
}
byteStore ??= MemoryByteStore();
performanceLog ??= PerformanceLog(StringBuffer());
@ -104,6 +116,12 @@ class ContextBuilderImpl implements ContextBuilder {
var options = _getAnalysisOptions(contextRoot, sourceFactory);
if (updateAnalysisOptions != null) {
updateAnalysisOptions(options);
} else if (updateAnalysisOptions2 != null) {
updateAnalysisOptions2(
analysisOptions: options,
contextRoot: contextRoot,
sdk: sdk,
);
}
final analysisContext =

View file

@ -116,7 +116,11 @@ class LintDriver {
sdkPath: options.dartSdkPath,
includedPaths:
files.map((file) => _absoluteNormalizedPath(file.path)).toList(),
updateAnalysisOptions: (analysisOptions) {
updateAnalysisOptions2: ({
required analysisOptions,
required contextRoot,
required sdk,
}) {
_updateAnalyzerOptions(analysisOptions, options);
},
);

View file

@ -4,6 +4,7 @@
import 'package:analyzer/dart/analysis/analysis_context.dart';
import 'package:analyzer/dart/analysis/analysis_context_collection.dart';
import 'package:analyzer/dart/analysis/context_root.dart';
import 'package:analyzer/dart/analysis/results.dart';
import 'package:analyzer/file_system/file_system.dart';
import 'package:analyzer/src/dart/analysis/analysis_context_collection.dart';
@ -12,6 +13,7 @@ import 'package:analyzer/src/dart/analysis/driver.dart';
import 'package:analyzer/src/dart/analysis/driver_based_analysis_context.dart';
import 'package:analyzer/src/dart/analysis/experiments.dart';
import 'package:analyzer/src/generated/engine.dart' show AnalysisOptionsImpl;
import 'package:analyzer/src/generated/sdk.dart';
import 'package:analyzer/src/summary2/kernel_compilation_service.dart';
import 'package:analyzer/src/test_utilities/mock_packages.dart';
import 'package:analyzer/src/test_utilities/mock_sdk.dart';
@ -234,7 +236,11 @@ abstract class ContextResolutionTest
/// Override this method to update [analysisOptions] for every context root,
/// the default or already updated with `analysis_options.yaml` file.
void updateAnalysisOptions(AnalysisOptionsImpl analysisOptions) {}
void updateAnalysisOptions({
required AnalysisOptionsImpl analysisOptions,
required ContextRoot contextRoot,
required DartSdk sdk,
}) {}
/// Call this method if the test needs to use the empty byte store, without
/// any information cached.
@ -267,7 +273,7 @@ abstract class ContextResolutionTest
sdkPath: sdkRoot.path,
sdkSummaryPath: sdkSummaryFile?.path,
librarySummaryPaths: librarySummaryFiles?.map((e) => e.path).toList(),
updateAnalysisOptions: updateAnalysisOptions,
updateAnalysisOptions2: updateAnalysisOptions,
);
verifyCreatedCollection();

View file

@ -2,8 +2,10 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'package:analyzer/dart/analysis/context_root.dart';
import 'package:analyzer/src/error/codes.dart';
import 'package:analyzer/src/generated/engine.dart';
import 'package:analyzer/src/generated/sdk.dart';
import 'package:pub_semver/src/version_constraint.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
@ -72,7 +74,11 @@ void f() {}
}
@override
void updateAnalysisOptions(AnalysisOptionsImpl analysisOptions) {
void updateAnalysisOptions({
required AnalysisOptionsImpl analysisOptions,
required ContextRoot contextRoot,
required DartSdk sdk,
}) {
analysisOptions.sourceLanguageConstraint =
VersionConstraint.parse('>= 2.12.0');
}

View file

@ -4,6 +4,7 @@
import 'dart:io' as io;
import 'package:analyzer/dart/analysis/context_root.dart';
import 'package:analyzer/dart/sdk/build_sdk_summary.dart';
import 'package:analyzer/error/error.dart';
import 'package:analyzer/error/listener.dart';
@ -17,6 +18,7 @@ import 'package:analyzer/src/dart/analysis/file_content_cache.dart';
import 'package:analyzer/src/dart/analysis/file_state.dart';
import 'package:analyzer/src/dart/analysis/results.dart';
import 'package:analyzer/src/generated/engine.dart';
import 'package:analyzer/src/generated/sdk.dart';
import 'package:analyzer/src/generated/source.dart';
import 'package:analyzer/src/lint/linter.dart';
import 'package:analyzer/src/lint/pub.dart';
@ -553,7 +555,7 @@ class _AnalysisContextProvider {
packagesFile: _commandLineOptions!.defaultPackagesPath,
resourceProvider: _resourceProvider,
sdkPath: _commandLineOptions!.dartSdkPath,
updateAnalysisOptions: _updateAnalysisOptions,
updateAnalysisOptions2: _updateAnalysisOptions,
fileContentCache: _fileContentCache,
);
@ -578,7 +580,11 @@ class _AnalysisContextProvider {
_analysisContext = _collection!.contextFor(path);
}
void _updateAnalysisOptions(AnalysisOptionsImpl analysisOptions) {
void _updateAnalysisOptions({
required AnalysisOptionsImpl analysisOptions,
required ContextRoot contextRoot,
required DartSdk sdk,
}) {
_commandLineOptions!.updateAnalysisOptions(analysisOptions);
}
}

View file

@ -17,6 +17,7 @@ import 'package:args/args.dart';
import 'package:path/path.dart' as path;
import 'core.dart';
import 'experiments.dart';
import 'sdk.dart';
import 'utils.dart';
@ -33,6 +34,7 @@ class AnalysisServer {
this.cacheDirectoryPath,
required this.commandName,
required this.argResults,
this.enabledExperiments = const [],
});
final String? cacheDirectoryPath;
@ -41,6 +43,7 @@ class AnalysisServer {
final List<FileSystemEntity> analysisRoots;
final String commandName;
final ArgResults? argResults;
final List<String> enabledExperiments;
Process? _process;
@ -103,6 +106,8 @@ class AnalysisServer {
sdkPath.path,
if (cacheDirectoryPath != null) '--cache=$cacheDirectoryPath',
if (packagesFile != null) '--packages=${packagesFile!.path}',
if (enabledExperiments.isNotEmpty)
'--$experimentFlagName=${enabledExperiments.join(',')}'
];
final process = await startDartProcess(sdk, command);

View file

@ -6,12 +6,14 @@ import 'dart:async';
import 'dart:convert';
import 'dart:io' as io;
import 'package:analyzer/src/dart/analysis/experiments.dart';
import 'package:cli_util/cli_logging.dart';
import 'package:meta/meta.dart';
import 'package:path/path.dart' as path;
import '../analysis_server.dart';
import '../core.dart';
import '../experiments.dart';
import '../sdk.dart';
import '../utils.dart';
@ -80,7 +82,8 @@ class AnalyzeCommand extends DartdevCommand {
valueHelp: 'path',
help: 'The path to the Dart SDK.',
hide: !verbose,
);
)
..addExperimentalFlags();
}
@override
@ -110,12 +113,6 @@ class AnalyzeCommand extends DartdevCommand {
final machineFormat = args['format'] == 'machine';
final jsonFormat = args['format'] == 'json';
final targetsNames =
targets.map((entity) => path.basename(entity.path)).join(', ');
var progress =
machineFormat ? null : log.progress('Analyzing $targetsNames');
io.Directory sdkPath;
if (args.wasParsed('sdk-path')) {
sdkPath = io.Directory(args['sdk-path'] as String);
@ -137,6 +134,26 @@ class AnalyzeCommand extends DartdevCommand {
sdkPath = io.Directory(sdk.sdkPath);
}
final experimentNames = {
for (var experiment in args.enabledExperiments)
if (experiment.startsWith('no-'))
experiment.substring(3)
else
experiment
};
final unknownExperiments =
experimentNames.difference(ExperimentStatus.knownFeatures.keys.toSet());
if (unknownExperiments.isNotEmpty) {
final unknownExperimentsText =
unknownExperiments.map((e) => "'$e'").join(', ');
usageException('Unknown experiment(s): $unknownExperimentsText');
}
final targetsNames =
targets.map((entity) => path.basename(entity.path)).join(', ');
final progress =
machineFormat ? null : log.progress('Analyzing $targetsNames');
final AnalysisServer server = AnalysisServer(
_packagesFile(),
sdkPath,
@ -144,6 +161,7 @@ class AnalyzeCommand extends DartdevCommand {
cacheDirectoryPath: args['cache'],
commandName: 'analyze',
argResults: args,
enabledExperiments: args.enabledExperiments,
);
server.onErrors.listen((FileAnalysisErrors fileErrors) {

View file

@ -123,8 +123,8 @@ class CompileSnapshotCommand extends CompileSubcommandCommand {
help: defineOption.help,
abbr: defineOption.abbr,
valueHelp: defineOption.valueHelp,
);
addExperimentalFlags(argParser, verbose);
)
..addExperimentalFlags(verbose: verbose);
}
@override
@ -255,9 +255,8 @@ Remove debugging information from the output and save it separately to the speci
help: 'Pass additional options to gen_snapshot.',
hide: true,
valueHelp: 'opt1,opt2,...',
);
addExperimentalFlags(argParser, verbose);
)
..addExperimentalFlags(verbose: verbose);
}
@override

View file

@ -179,15 +179,15 @@ class RunCommand extends DartdevCommand {
hide: !verbose,
help:
'Use the Dart Development Service (DDS) for enhanced debugging '
'functionality. Note: Disabling DDS may break some functionality '
'in IDEs and other tooling.',
'functionality. Note: Disabling DDS may break some '
'functionality in IDEs and other tooling.',
defaultsTo: true)
..addFlag(
'debug-dds',
hide: true,
);
}
addExperimentalFlags(argParser, verbose);
argParser.addExperimentalFlags(verbose: verbose);
}
@override

View file

@ -9,53 +9,53 @@ import 'package:collection/collection.dart' show IterableExtension;
const experimentFlagName = 'enable-experiment';
/// Return a list of all the non-expired Dart experiments.
List<ExperimentalFeature> get experimentalFeatures {
List<ExperimentalFeature> features = ExperimentStatus.knownFeatures.values
.where((feature) => !feature.isExpired)
.toList();
features.sort((a, b) => a.enableString.compareTo(b.enableString));
return features;
}
List<ExperimentalFeature> get experimentalFeatures =>
ExperimentStatus.knownFeatures.values
.where((feature) => !feature.isExpired)
.toList()
..sort((a, b) => a.enableString.compareTo(b.enableString));
void addExperimentalFlags(ArgParser argParser, bool verbose) {
List<ExperimentalFeature> features = experimentalFeatures;
extension ArgParserExtensions on ArgParser {
void addExperimentalFlags({bool verbose = false}) {
List<ExperimentalFeature> features = experimentalFeatures;
Map<String, String> allowedHelp = {};
for (ExperimentalFeature feature in features) {
String suffix =
feature.isEnabledByDefault ? ' (no-op - enabled by default)' : '';
allowedHelp[feature.enableString] = '${feature.documentation}$suffix';
Map<String, String> allowedHelp = {};
for (ExperimentalFeature feature in features) {
String suffix =
feature.isEnabledByDefault ? ' (no-op - enabled by default)' : '';
allowedHelp[feature.enableString] = '${feature.documentation}$suffix';
}
addMultiOption(
experimentFlagName,
valueHelp: 'experiment',
allowedHelp: verbose ? allowedHelp : null,
help: 'Enable one or more experimental features '
'(see dart.dev/go/experiments).',
hide: !verbose,
);
}
argParser.addMultiOption(
experimentFlagName,
valueHelp: 'experiment',
allowedHelp: verbose ? allowedHelp : null,
help: 'Enable one or more experimental features '
'(see dart.dev/go/experiments).',
hide: !verbose,
);
}
extension EnabledExperimentsArg on ArgResults {
extension ArgResultsExtensions on ArgResults {
List<String> get enabledExperiments {
List<String> enabledExperiments = [];
// Check to see if the ArgParser which generated this result accepts
// --enable-experiment as an option. If so, return the result if it was
// `--enable-experiment` as an option. If so, return the result if it was
// provided.
if (options.contains(experimentFlagName)) {
if (wasParsed(experimentFlagName)) {
enabledExperiments = this[experimentFlagName];
enabledExperiments = this[experimentFlagName] as List<String>;
}
} else {
// In the case where a command uses ArgParser.allowAnything() as its
// parser the valid set of options for the command isn't specified and
// In the case where a command uses `ArgParser.allowAnything()` as its
// parser, the valid set of options for the command isn't specified and
// isn't enforced. Instead, we have to manually parse the arguments to
// look for --enable-experiment=. Currently, this path is only taken for
// the pub and test commands, as well as when we are trying to send
// look for `--enable-experiment=`. Currently, this path is only taken for
// the `pub` and `test` commands, as well as when we are trying to send
// analytics.
final String? experiments = arguments.firstWhereOrNull(
(e) => e.startsWith('--enable-experiment='),
final experiments = arguments.firstWhereOrNull(
(e) => e.startsWith('--$experimentFlagName='),
);
if (experiments == null) {
return [];
@ -63,7 +63,7 @@ extension EnabledExperimentsArg on ArgResults {
enabledExperiments = experiments.split('=')[1].split(',');
}
for (ExperimentalFeature feature in experimentalFeatures) {
for (final feature in experimentalFeatures) {
// We allow default true flags, but complain when they are passed in.
if (feature.isEnabledByDefault &&
enabledExperiments.contains(feature.enableString)) {

View file

@ -421,6 +421,26 @@ void defineAnalyze() {
expect(result.stderr, isEmpty);
});
test('--enable-experiment', () async {
p = project(mainSrc: 'final x = List.filled(growable: true, 7, "");');
var result = await p
.run(['analyze', '--enable-experiment=no-named-arguments-anywhere']);
expect(result.exitCode, 3);
expect(result.stdout,
contains('Positional arguments must occur before named arguments.'));
expect(result.stderr, isEmpty);
});
test('--enable-experiment with a bad experiment', () async {
p = project();
var result = await p.run(['analyze', '--enable-experiment=bad']);
expect(result.exitCode, 64);
expect(result.stdout, isEmpty);
expect(result.stderr, contains("Unknown experiment(s): 'bad'"));
});
test('--verbose', () async {
p = project(mainSrc: '''
int f() {