Chromium Code Reviews| 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..4a85f046f31238ba79f5bd8cf7809bbac9159ad7 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 enable_super_mixins = 'enableSuperMixins'; |
|
Brian Wilkerson
2015/10/29 20:24:03
Shouldn't we be using camel case for variable name
pquitslund
2015/10/29 21:06:18
Yes! Fixed.
|
| 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 [ |
| errors, |
| exclude, |
| + language, |
| plugins, |
| strong_mode |
| ]; |
| + |
| + /// Supported `analyzer` language configuration options. |
| + static const List<String> languageOptions = const [enable_super_mixins]; |
| +} |
| + |
| +/// 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.top_level); |
| +} |
| + |
| /// 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.enable_super_mixins) { |
| + 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; |
| + } |
| + } |
| +} |