mirror of
https://github.com/dart-lang/sdk
synced 2024-10-04 18:36:29 +00:00
Embedded options discovery (#24943).
Merges analysis options extracted from `_embedder.yaml` files (potentially) with local overrides. Background: https://github.com/dart-lang/sdk/issues/24943 For test coverage of merging, see: `analyzer/test/src/util/yaml_test.dart`. BUG=24943 R=brianwilkerson@google.com, johnmccutchan@google.com Review URL: https://codereview.chromium.org/1445363002 .
This commit is contained in:
parent
3d43d6d51c
commit
3a1a3ee094
|
@ -28,6 +28,7 @@ import 'package:analyzer/src/generated/source.dart';
|
|||
import 'package:analyzer/src/generated/source_io.dart';
|
||||
import 'package:analyzer/src/task/options.dart';
|
||||
import 'package:analyzer/src/util/absolute_path.dart';
|
||||
import 'package:analyzer/src/util/yaml.dart';
|
||||
import 'package:package_config/packages.dart';
|
||||
import 'package:package_config/packages_file.dart' as pkgfile show parse;
|
||||
import 'package:package_config/src/packages_impl.dart' show MapPackages;
|
||||
|
@ -515,15 +516,28 @@ class ContextManagerImpl implements ContextManager {
|
|||
}
|
||||
});
|
||||
|
||||
// In case options files are removed, revert to default options.
|
||||
// In case options files are removed, revert to defaults.
|
||||
if (optionsRemoved) {
|
||||
// Start with defaults.
|
||||
info.context.analysisOptions = new AnalysisOptionsImpl();
|
||||
|
||||
// Apply inherited options.
|
||||
YamlMap embeddedOptions = _getEmbeddedOptions(info.context);
|
||||
if (embeddedOptions != null) {
|
||||
configureContextOptions(info.context, embeddedOptions);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for embedded options.
|
||||
YamlMap embeddedOptions = _getEmbeddedOptions(info.context);
|
||||
if (embeddedOptions != null) {
|
||||
options = new Merger().merge(embeddedOptions, options);
|
||||
}
|
||||
|
||||
// Analysis options are processed 'in-line'.
|
||||
var analyzer = options[AnalyzerOptions.analyzer];
|
||||
if (analyzer is! YamlMap) {
|
||||
if (analyzer is! Map) {
|
||||
// No options for analyzer.
|
||||
return;
|
||||
}
|
||||
|
@ -1023,6 +1037,20 @@ class ContextManagerImpl implements ContextManager {
|
|||
return packageSpec;
|
||||
}
|
||||
|
||||
/// Get analysis options associated with an `_embedder.yaml`. If there is
|
||||
/// more than one `_embedder.yaml` associated with the given context, `null`
|
||||
/// is returned.
|
||||
YamlMap _getEmbeddedOptions(AnalysisContext context) {
|
||||
if (context is InternalAnalysisContext) {
|
||||
EmbedderYamlLocator locator = context.embedderYamlLocator;
|
||||
Iterable<YamlMap> maps = locator.embedderYamls.values;
|
||||
if (maps.length == 1) {
|
||||
return maps.first;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the [ContextInfo] for the "innermost" context whose associated
|
||||
* folder is or contains the given path. ("innermost" refers to the nesting
|
||||
|
|
|
@ -93,6 +93,80 @@ class AbstractContextManagerTest {
|
|||
resourceProvider.newFolder(projPath);
|
||||
}
|
||||
|
||||
test_embedder_options() async {
|
||||
// Create files.
|
||||
String libPath = newFolder([projPath, LIB_NAME]);
|
||||
String sdkExtPath = newFolder([projPath, 'sdk_ext']);
|
||||
newFile([projPath, 'test', 'test.dart']);
|
||||
newFile([sdkExtPath, 'entry.dart']);
|
||||
// Setup _embedder.yaml.
|
||||
newFile(
|
||||
[libPath, '_embedder.yaml'],
|
||||
r'''
|
||||
embedder_libs:
|
||||
"dart:foobar": "../sdk_ext/entry.dart"
|
||||
analyzer:
|
||||
strong-mode: true
|
||||
language:
|
||||
enableSuperMixins: true
|
||||
''');
|
||||
// Setup .packages file
|
||||
newFile(
|
||||
[projPath, '.packages'],
|
||||
r'''
|
||||
test_pack:lib/''');
|
||||
|
||||
// Setup .analysis_options
|
||||
newFile(
|
||||
[projPath, AnalysisEngine.ANALYSIS_OPTIONS_FILE],
|
||||
r'''
|
||||
analyzer:
|
||||
exclude:
|
||||
- 'test/**'
|
||||
language:
|
||||
enableGenericMethods: true
|
||||
errors:
|
||||
unused_local_variable: false
|
||||
''');
|
||||
|
||||
// Setup context.
|
||||
manager.setRoots(<String>[projPath], <String>[], <String, String>{});
|
||||
await pumpEventQueue();
|
||||
|
||||
// Confirm that one context was created.
|
||||
var contexts =
|
||||
manager.contextsInAnalysisRoot(resourceProvider.newFolder(projPath));
|
||||
expect(contexts, isNotNull);
|
||||
expect(contexts, hasLength(1));
|
||||
var context = contexts[0];
|
||||
|
||||
// Verify options.
|
||||
// * from `_embedder.yaml`:
|
||||
expect(context.analysisOptions.strongMode, isTrue);
|
||||
expect(context.analysisOptions.enableSuperMixins, isTrue);
|
||||
// * from `.analysis_options`:
|
||||
expect(context.analysisOptions.enableGenericMethods, isTrue);
|
||||
// verify tests are excluded
|
||||
expect(callbacks.currentContextFilePaths[projPath].keys,
|
||||
['/my/proj/sdk_ext/entry.dart']);
|
||||
|
||||
// Verify filter setup.
|
||||
List<ErrorFilter> filters =
|
||||
callbacks.currentContext.getConfigurationData(CONFIGURED_ERROR_FILTERS);
|
||||
expect(filters, hasLength(1));
|
||||
expect(
|
||||
filters.first(new AnalysisError(
|
||||
new TestSource(), 0, 1, HintCode.UNUSED_LOCAL_VARIABLE, [
|
||||
['x']
|
||||
])),
|
||||
isTrue);
|
||||
|
||||
// Sanity check embedder libs.
|
||||
var source = context.sourceFactory.forUri('dart:foobar');
|
||||
expect(source, isNotNull);
|
||||
expect(source.fullName, '/my/proj/sdk_ext/entry.dart');
|
||||
}
|
||||
|
||||
test_analysis_options_parse_failure() async {
|
||||
// Create files.
|
||||
String libPath = newFolder([projPath, LIB_NAME]);
|
||||
|
@ -138,6 +212,49 @@ class AbstractContextManagerTest {
|
|||
expect(contexts, contains(subProjContextInfo.context));
|
||||
}
|
||||
|
||||
test_embedder_packagespec() async {
|
||||
// Create files.
|
||||
String libPath = newFolder([projPath, LIB_NAME]);
|
||||
newFile([libPath, 'main.dart']);
|
||||
newFile([libPath, 'nope.dart']);
|
||||
String sdkExtPath = newFolder([projPath, 'sdk_ext']);
|
||||
newFile([sdkExtPath, 'entry.dart']);
|
||||
String sdkExtSrcPath = newFolder([projPath, 'sdk_ext', 'src']);
|
||||
newFile([sdkExtSrcPath, 'part.dart']);
|
||||
// Setup _embedder.yaml.
|
||||
newFile(
|
||||
[libPath, '_embedder.yaml'],
|
||||
r'''
|
||||
embedder_libs:
|
||||
"dart:foobar": "../sdk_ext/entry.dart"
|
||||
"dart:typed_data": "../sdk_ext/src/part"
|
||||
''');
|
||||
// Setup .packages file
|
||||
newFile(
|
||||
[projPath, '.packages'],
|
||||
r'''
|
||||
test_pack:lib/''');
|
||||
// Setup context.
|
||||
|
||||
manager.setRoots(<String>[projPath], <String>[], <String, String>{});
|
||||
await pumpEventQueue();
|
||||
// Confirm that one context was created.
|
||||
var contexts =
|
||||
manager.contextsInAnalysisRoot(resourceProvider.newFolder(projPath));
|
||||
expect(contexts, isNotNull);
|
||||
expect(contexts.length, equals(1));
|
||||
var context = contexts[0];
|
||||
var source = context.sourceFactory.forUri('dart:foobar');
|
||||
expect(source, isNotNull);
|
||||
expect(source.fullName, equals('/my/proj/sdk_ext/entry.dart'));
|
||||
// We can't find dart:core because we didn't list it in our
|
||||
// embedder_libs map.
|
||||
expect(context.sourceFactory.forUri('dart:core'), isNull);
|
||||
// We can find dart:typed_data because we listed it in our
|
||||
// embedder_libs map.
|
||||
expect(context.sourceFactory.forUri('dart:typed_data'), isNotNull);
|
||||
}
|
||||
|
||||
test_error_filter_analysis_option() async {
|
||||
// Create files.
|
||||
newFile(
|
||||
|
@ -568,49 +685,6 @@ analyzer:
|
|||
});
|
||||
}
|
||||
|
||||
test_embedder_packagespec() async {
|
||||
// Create files.
|
||||
String libPath = newFolder([projPath, LIB_NAME]);
|
||||
newFile([libPath, 'main.dart']);
|
||||
newFile([libPath, 'nope.dart']);
|
||||
String sdkExtPath = newFolder([projPath, 'sdk_ext']);
|
||||
newFile([sdkExtPath, 'entry.dart']);
|
||||
String sdkExtSrcPath = newFolder([projPath, 'sdk_ext', 'src']);
|
||||
newFile([sdkExtSrcPath, 'part.dart']);
|
||||
// Setup _embedder.yaml.
|
||||
newFile(
|
||||
[libPath, '_embedder.yaml'],
|
||||
r'''
|
||||
embedder_libs:
|
||||
"dart:foobar": "../sdk_ext/entry.dart"
|
||||
"dart:typed_data": "../sdk_ext/src/part"
|
||||
''');
|
||||
// Setup .packages file
|
||||
newFile(
|
||||
[projPath, '.packages'],
|
||||
r'''
|
||||
test_pack:lib/''');
|
||||
// Setup context.
|
||||
|
||||
manager.setRoots(<String>[projPath], <String>[], <String, String>{});
|
||||
await pumpEventQueue();
|
||||
// Confirm that one context was created.
|
||||
var contexts =
|
||||
manager.contextsInAnalysisRoot(resourceProvider.newFolder(projPath));
|
||||
expect(contexts, isNotNull);
|
||||
expect(contexts.length, equals(1));
|
||||
var context = contexts[0];
|
||||
var source = context.sourceFactory.forUri('dart:foobar');
|
||||
expect(source, isNotNull);
|
||||
expect(source.fullName, equals('/my/proj/sdk_ext/entry.dart'));
|
||||
// We can't find dart:core because we didn't list it in our
|
||||
// embedder_libs map.
|
||||
expect(context.sourceFactory.forUri('dart:core'), isNull);
|
||||
// We can find dart:typed_data because we listed it in our
|
||||
// embedder_libs map.
|
||||
expect(context.sourceFactory.forUri('dart:typed_data'), isNotNull);
|
||||
}
|
||||
|
||||
test_sdk_ext_packagespec() async {
|
||||
// Create files.
|
||||
String libPath = newFolder([projPath, LIB_NAME]);
|
||||
|
@ -1943,19 +2017,6 @@ class TestContextManagerCallbacks extends ContextManagerCallbacks {
|
|||
*/
|
||||
Iterable<String> get currentContextPaths => currentContextTimestamps.keys;
|
||||
|
||||
/// If [disposition] has a package map, attempt to locate `_embedder.yaml`
|
||||
/// files.
|
||||
void _locateEmbedderYamls(InternalAnalysisContext context,
|
||||
FolderDisposition disposition) {
|
||||
Map<String, List<Folder>> packageMap;
|
||||
if (disposition is PackageMapDisposition) {
|
||||
packageMap = disposition.packageMap;
|
||||
} else if (disposition is PackagesFileDisposition) {
|
||||
packageMap = disposition.buildPackageMap(resourceProvider);
|
||||
}
|
||||
context.embedderYamlLocator.refresh(packageMap);
|
||||
}
|
||||
|
||||
@override
|
||||
AnalysisContext addContext(Folder folder, FolderDisposition disposition) {
|
||||
String path = folder.path;
|
||||
|
@ -2046,6 +2107,19 @@ class TestContextManagerCallbacks extends ContextManagerCallbacks {
|
|||
Folder contextFolder, FolderDisposition disposition) {
|
||||
currentContextDispositions[contextFolder.path] = disposition;
|
||||
}
|
||||
|
||||
/// If [disposition] has a package map, attempt to locate `_embedder.yaml`
|
||||
/// files.
|
||||
void _locateEmbedderYamls(
|
||||
InternalAnalysisContext context, FolderDisposition disposition) {
|
||||
Map<String, List<Folder>> packageMap;
|
||||
if (disposition is PackageMapDisposition) {
|
||||
packageMap = disposition.packageMap;
|
||||
} else if (disposition is PackagesFileDisposition) {
|
||||
packageMap = disposition.buildPackageMap(resourceProvider);
|
||||
}
|
||||
context.embedderYamlLocator.refresh(packageMap);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -227,28 +227,6 @@ class GenerateOptionsErrorsTask extends SourceBasedAnalysisTask {
|
|||
new GenerateOptionsErrorsTask(context, target);
|
||||
}
|
||||
|
||||
/// Validates `analyzer` strong-mode value configuration options.
|
||||
class StrongModeOptionValueValidator extends OptionsValidator {
|
||||
ErrorBuilder trueOrFalseBuilder = new TrueOrFalseValueErrorBuilder();
|
||||
|
||||
@override
|
||||
void validate(ErrorReporter reporter, Map<String, YamlNode> options) {
|
||||
var analyzer = options[AnalyzerOptions.analyzer];
|
||||
if (analyzer is! YamlMap) {
|
||||
return;
|
||||
}
|
||||
|
||||
var v = analyzer.nodes[AnalyzerOptions.strong_mode];
|
||||
if (v is YamlScalar) {
|
||||
var value = toLowerCase(v.value);
|
||||
if (!AnalyzerOptions.trueOrFalse.contains(value)) {
|
||||
trueOrFalseBuilder.reportError(
|
||||
reporter, AnalyzerOptions.strong_mode, v);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Validates `analyzer` language configuration options.
|
||||
class LanguageOptionValidator extends OptionsValidator {
|
||||
ErrorBuilder builder = new ErrorBuilder(AnalyzerOptions.languageOptions);
|
||||
|
@ -313,6 +291,28 @@ class OptionsFileValidator {
|
|||
}
|
||||
}
|
||||
|
||||
/// Validates `analyzer` strong-mode value configuration options.
|
||||
class StrongModeOptionValueValidator extends OptionsValidator {
|
||||
ErrorBuilder trueOrFalseBuilder = new TrueOrFalseValueErrorBuilder();
|
||||
|
||||
@override
|
||||
void validate(ErrorReporter reporter, Map<String, YamlNode> options) {
|
||||
var analyzer = options[AnalyzerOptions.analyzer];
|
||||
if (analyzer is! YamlMap) {
|
||||
return;
|
||||
}
|
||||
|
||||
var v = analyzer.nodes[AnalyzerOptions.strong_mode];
|
||||
if (v is YamlScalar) {
|
||||
var value = toLowerCase(v.value);
|
||||
if (!AnalyzerOptions.trueOrFalse.contains(value)) {
|
||||
trueOrFalseBuilder.reportError(
|
||||
reporter, AnalyzerOptions.strong_mode, v);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Validates `analyzer` top-level options.
|
||||
class TopLevelAnalyzerOptionsValidator extends TopLevelOptionValidator {
|
||||
TopLevelAnalyzerOptionsValidator()
|
||||
|
@ -372,7 +372,7 @@ class _OptionsProcessor {
|
|||
}
|
||||
|
||||
var analyzer = options[AnalyzerOptions.analyzer];
|
||||
if (analyzer is! YamlMap) {
|
||||
if (analyzer is! Map) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -409,30 +409,36 @@ class _OptionsProcessor {
|
|||
context.setConfigurationData(CONFIGURED_ERROR_FILTERS, filters);
|
||||
}
|
||||
|
||||
void setLanguageOption(
|
||||
AnalysisContext context, Object feature, Object value) {
|
||||
if (feature == AnalyzerOptions.enableSuperMixins) {
|
||||
if (isTrue(value)) {
|
||||
AnalysisOptionsImpl options =
|
||||
new AnalysisOptionsImpl.from(context.analysisOptions);
|
||||
options.enableSuperMixins = true;
|
||||
context.analysisOptions = options;
|
||||
}
|
||||
}
|
||||
if (feature == AnalyzerOptions.enableGenericMethods) {
|
||||
if (isTrue(value)) {
|
||||
AnalysisOptionsImpl options =
|
||||
new AnalysisOptionsImpl.from(context.analysisOptions);
|
||||
options.enableGenericMethods = true;
|
||||
context.analysisOptions = options;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void setLanguageOptions(AnalysisContext context, Object configs) {
|
||||
if (configs is YamlMap) {
|
||||
configs.nodes.forEach((k, v) {
|
||||
String feature;
|
||||
if (k is YamlScalar && v is YamlScalar) {
|
||||
feature = k.value?.toString();
|
||||
if (feature == AnalyzerOptions.enableSuperMixins) {
|
||||
if (isTrue(v.value)) {
|
||||
AnalysisOptionsImpl options =
|
||||
new AnalysisOptionsImpl.from(context.analysisOptions);
|
||||
options.enableSuperMixins = true;
|
||||
context.analysisOptions = options;
|
||||
}
|
||||
}
|
||||
if (feature == AnalyzerOptions.enableGenericMethods) {
|
||||
if (isTrue(v.value)) {
|
||||
AnalysisOptionsImpl options =
|
||||
new AnalysisOptionsImpl.from(context.analysisOptions);
|
||||
options.enableGenericMethods = true;
|
||||
context.analysisOptions = options;
|
||||
}
|
||||
}
|
||||
String feature = k.value?.toString();
|
||||
setLanguageOption(context, feature, v.value);
|
||||
}
|
||||
});
|
||||
} else if (configs is Map) {
|
||||
configs.forEach((k, v) => setLanguageOption(context, k, v));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue