| Index: pkg/analyzer/lib/src/task/options.dart
|
| diff --git a/pkg/analyzer/lib/src/task/options.dart b/pkg/analyzer/lib/src/task/options.dart
|
| index fb24b0e47215d0cb05f50ad98b271aa13197130c..689bff2eebbd5b212bf0decb42562883051f97a4 100644
|
| --- a/pkg/analyzer/lib/src/task/options.dart
|
| +++ b/pkg/analyzer/lib/src/task/options.dart
|
| @@ -10,6 +10,7 @@ import 'package:analyzer/source/analysis_options_provider.dart';
|
| import 'package:analyzer/src/generated/engine.dart';
|
| import 'package:analyzer/src/generated/java_engine.dart';
|
| import 'package:analyzer/src/generated/source.dart';
|
| +import 'package:analyzer/src/generated/utilities_general.dart';
|
| import 'package:analyzer/src/task/general.dart';
|
| import 'package:analyzer/task/general.dart';
|
| import 'package:analyzer/task/model.dart';
|
| @@ -23,11 +24,21 @@ final ListResultDescriptor<AnalysisError> ANALYSIS_OPTIONS_ERRORS =
|
| new ListResultDescriptor<AnalysisError>(
|
| 'ANALYSIS_OPTIONS_ERRORS', AnalysisError.NO_ERRORS);
|
|
|
| +final _OptionsProcessor _processor = new _OptionsProcessor();
|
| +
|
| +/// Configure this [context] based on configuration details specified in
|
| +/// the given [options].
|
| +void configureContextOptions(
|
| + AnalysisContext context, Map<String, YamlNode> options) =>
|
| + _processor.configure(context, options);
|
| +
|
| /// `analyzer` analysis options constants.
|
| class AnalyzerOptions {
|
| static const String analyzer = 'analyzer';
|
| + static const String enableSuperMixins = 'enableSuperMixins';
|
| static const String errors = 'errors';
|
| static const String exclude = 'exclude';
|
| + static const String language = 'language';
|
| static const String plugins = 'plugins';
|
| static const String strong_mode = 'strong-mode';
|
|
|
| @@ -37,13 +48,69 @@ class AnalyzerOptions {
|
| /// Ways to say `include`.
|
| static const List<String> includeSynonyms = const ['include', 'true'];
|
|
|
| + /// Ways to say `true` or `false`.
|
| + static const List<String> trueOrFalse = const ['true', 'false'];
|
| +
|
| /// Supported top-level `analyzer` options.
|
| - static const List<String> top_level = const [
|
| + static const List<String> topLevel = const [
|
| errors,
|
| exclude,
|
| + language,
|
| plugins,
|
| strong_mode
|
| ];
|
| +
|
| + /// Supported `analyzer` language configuration options.
|
| + static const List<String> languageOptions = const [enableSuperMixins];
|
| +}
|
| +
|
| +/// Validates `analyzer` options.
|
| +class AnalyzerOptionsValidator extends CompositeValidator {
|
| + AnalyzerOptionsValidator()
|
| + : super([
|
| + new TopLevelAnalyzerOptionsValidator(),
|
| + new ErrorFilterOptionValidator(),
|
| + new LanguageOptionValidator()
|
| + ]);
|
| +}
|
| +
|
| +/// Convenience class for composing validators.
|
| +class CompositeValidator extends OptionsValidator {
|
| + final List<OptionsValidator> validators;
|
| + CompositeValidator(this.validators);
|
| +
|
| + @override
|
| + void validate(ErrorReporter reporter, Map<String, YamlNode> options) =>
|
| + validators.forEach((v) => v.validate(reporter, options));
|
| +}
|
| +
|
| +/// Builds error reports with value proposals.
|
| +class ErrorBuilder {
|
| + String proposal;
|
| + AnalysisOptionsWarningCode code;
|
| +
|
| + /// Create a builder for the given [supportedOptions].
|
| + ErrorBuilder(List<String> supportedOptions) {
|
| + assert(supportedOptions != null && !supportedOptions.isEmpty);
|
| + if (supportedOptions.length > 1) {
|
| + proposal = StringUtilities.printListOfQuotedNames(supportedOptions);
|
| + code = pluralProposalCode;
|
| + } else {
|
| + proposal = "'${supportedOptions.join()}'";
|
| + code = singularProposalCode;
|
| + }
|
| + }
|
| + AnalysisOptionsWarningCode get pluralProposalCode =>
|
| + AnalysisOptionsWarningCode.UNSUPPORTED_OPTION_WITH_LEGAL_VALUES;
|
| +
|
| + AnalysisOptionsWarningCode get singularProposalCode =>
|
| + AnalysisOptionsWarningCode.UNSUPPORTED_OPTION_WITH_LEGAL_VALUE;
|
| +
|
| + /// Report an unsupported [node] value, defined in the given [scopeName].
|
| + void reportError(ErrorReporter reporter, String scopeName, YamlNode node) {
|
| + reporter.reportErrorForSpan(
|
| + code, node.span, [scopeName, node.value, proposal]);
|
| + }
|
| }
|
|
|
| /// Validates `analyzer` error filter options.
|
| @@ -57,17 +124,15 @@ class ErrorFilterOptionValidator extends OptionsValidator {
|
| void validate(ErrorReporter reporter, Map<String, YamlNode> options) {
|
| YamlMap analyzer = options[AnalyzerOptions.analyzer];
|
| if (analyzer == null) {
|
| - // No options for analyzer.
|
| return;
|
| }
|
|
|
| YamlNode filters = analyzer[AnalyzerOptions.errors];
|
| -
|
| if (filters is YamlMap) {
|
| String value;
|
| filters.nodes.forEach((k, v) {
|
| if (k is YamlScalar) {
|
| - value = k.value?.toString()?.toUpperCase();
|
| + value = toUpperCase(k.value);
|
| if (!ErrorCode.values.any((ErrorCode code) => code.name == value)) {
|
| reporter.reportErrorForSpan(
|
| AnalysisOptionsWarningCode.UNRECOGNIZED_ERROR_CODE,
|
| @@ -76,7 +141,7 @@ class ErrorFilterOptionValidator extends OptionsValidator {
|
| }
|
| }
|
| if (v is YamlScalar) {
|
| - value = v.value?.toString()?.toLowerCase();
|
| + value = toLowerCase(v.value);
|
| if (!AnalyzerOptions.ignoreSynonyms.contains(value) &&
|
| !AnalyzerOptions.includeSynonyms.contains(value)) {
|
| reporter.reportErrorForSpan(
|
| @@ -84,39 +149,12 @@ class ErrorFilterOptionValidator extends OptionsValidator {
|
| v.span,
|
| [AnalyzerOptions.errors, v.value?.toString(), legalIncludes]);
|
| }
|
| -
|
| - value = v.value?.toString()?.toLowerCase();
|
| }
|
| });
|
| }
|
| }
|
| }
|
|
|
| -/// Validates `analyzer` top-level options.
|
| -class TopLevelAnalyzerOptionsValidator extends TopLevelOptionValidator {
|
| - TopLevelAnalyzerOptionsValidator()
|
| - : super(AnalyzerOptions.analyzer, AnalyzerOptions.top_level);
|
| -}
|
| -
|
| -/// Validates `analyzer` options.
|
| -class AnalyzerOptionsValidator extends CompositeValidator {
|
| - AnalyzerOptionsValidator()
|
| - : super([
|
| - new TopLevelAnalyzerOptionsValidator(),
|
| - new ErrorFilterOptionValidator()
|
| - ]);
|
| -}
|
| -
|
| -/// Convenience class for composing validators.
|
| -class CompositeValidator extends OptionsValidator {
|
| - final List<OptionsValidator> validators;
|
| - CompositeValidator(this.validators);
|
| -
|
| - @override
|
| - void validate(ErrorReporter reporter, Map<String, YamlNode> options) =>
|
| - validators.forEach((v) => v.validate(reporter, options));
|
| -}
|
| -
|
| /// A task that generates errors for an `.analysis_options` file.
|
| class GenerateOptionsErrorsTask extends SourceBasedAnalysisTask {
|
| /// The name of the input whose value is the content of the file.
|
| @@ -184,6 +222,43 @@ class GenerateOptionsErrorsTask extends SourceBasedAnalysisTask {
|
| new GenerateOptionsErrorsTask(context, target);
|
| }
|
|
|
| +/// Validates `analyzer` language configuration options.
|
| +class LanguageOptionValidator extends OptionsValidator {
|
| + ErrorBuilder builder = new ErrorBuilder(AnalyzerOptions.languageOptions);
|
| + ErrorBuilder trueOrFalseBuilder = new TrueOrFalseValueErrorBuilder();
|
| +
|
| + @override
|
| + void validate(ErrorReporter reporter, Map<String, YamlNode> options) {
|
| + YamlMap analyzer = options[AnalyzerOptions.analyzer];
|
| + if (analyzer == null) {
|
| + return;
|
| + }
|
| +
|
| + YamlNode language = analyzer[AnalyzerOptions.language];
|
| + if (language is YamlMap) {
|
| + language.nodes.forEach((k, v) {
|
| + String key, value;
|
| + bool validKey = false;
|
| + if (k is YamlScalar) {
|
| + key = k.value?.toString();
|
| + if (!AnalyzerOptions.languageOptions.contains(key)) {
|
| + builder.reportError(reporter, AnalyzerOptions.language, k);
|
| + } else {
|
| + // If we have a valid key, go on and check the value.
|
| + validKey = true;
|
| + }
|
| + }
|
| + if (validKey && v is YamlScalar) {
|
| + value = toLowerCase(v.value);
|
| + if (!AnalyzerOptions.trueOrFalse.contains(value)) {
|
| + trueOrFalseBuilder.reportError(reporter, key, v);
|
| + }
|
| + }
|
| + });
|
| + }
|
| + }
|
| +}
|
| +
|
| /// Validates `linter` top-level options.
|
| /// TODO(pq): move into `linter` package and plugin.
|
| class LinterOptionsValidator extends TopLevelOptionValidator {
|
| @@ -211,6 +286,12 @@ class OptionsFileValidator {
|
| }
|
| }
|
|
|
| +/// Validates `analyzer` top-level options.
|
| +class TopLevelAnalyzerOptionsValidator extends TopLevelOptionValidator {
|
| + TopLevelAnalyzerOptionsValidator()
|
| + : super(AnalyzerOptions.analyzer, AnalyzerOptions.topLevel);
|
| +}
|
| +
|
| /// Validates top-level options. For example,
|
| /// plugin:
|
| /// top-level-option: true
|
| @@ -248,3 +329,84 @@ class TopLevelOptionValidator extends OptionsValidator {
|
| }
|
| }
|
| }
|
| +
|
| +/// An error-builder that knows about `true` and `false` legal values.
|
| +class TrueOrFalseValueErrorBuilder extends ErrorBuilder {
|
| + TrueOrFalseValueErrorBuilder() : super(AnalyzerOptions.trueOrFalse);
|
| + @override
|
| + AnalysisOptionsWarningCode get pluralProposalCode =>
|
| + AnalysisOptionsWarningCode.UNSUPPORTED_VALUE;
|
| +}
|
| +
|
| +class _OptionsProcessor {
|
| + void configure(AnalysisContext context, Map<String, YamlNode> options) {
|
| + if (options == null) {
|
| + return;
|
| + }
|
| +
|
| + YamlMap analyzer = options[AnalyzerOptions.analyzer];
|
| + if (analyzer == null) {
|
| + return;
|
| + }
|
| +
|
| + // Set strong mode (default is false).
|
| + bool strongMode = analyzer[AnalyzerOptions.strong_mode] ?? false;
|
| + setStrongMode(context, strongMode);
|
| +
|
| + // Set filters.
|
| + YamlNode filters = analyzer[AnalyzerOptions.errors];
|
| + setFilters(context, filters);
|
| +
|
| + // Process language options.
|
| + YamlNode language = analyzer[AnalyzerOptions.language];
|
| + setLanguageOptions(context, language);
|
| + }
|
| +
|
| + void setFilters(AnalysisContext context, YamlNode codes) {
|
| + List<ErrorFilter> filters = <ErrorFilter>[];
|
| + // If codes are enumerated, collect them as filters; else leave filters
|
| + // empty to overwrite previous value.
|
| + if (codes is YamlMap) {
|
| + String value;
|
| + codes.nodes.forEach((k, v) {
|
| + if (k is YamlScalar && v is YamlScalar) {
|
| + value = toLowerCase(v.value);
|
| + if (AnalyzerOptions.ignoreSynonyms.contains(value)) {
|
| + // Case-insensitive.
|
| + String code = toUpperCase(k.value);
|
| + filters.add((AnalysisError error) => error.errorCode.name == code);
|
| + }
|
| + }
|
| + });
|
| + }
|
| + context.setConfigurationData(CONFIGURED_ERROR_FILTERS, filters);
|
| + }
|
| +
|
| + void setLanguageOptions(AnalysisContext context, YamlNode 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;
|
| + }
|
| + }
|
| + }
|
| + });
|
| + }
|
| + }
|
| +
|
| + void setStrongMode(AnalysisContext context, bool strongMode) {
|
| + if (context.analysisOptions.strongMode != strongMode) {
|
| + AnalysisOptionsImpl options =
|
| + new AnalysisOptionsImpl.from(context.analysisOptions);
|
| + options.strongMode = strongMode;
|
| + context.analysisOptions = options;
|
| + }
|
| + }
|
| +}
|
|
|