Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(562)

Unified Diff: packages/analyzer/lib/src/task/options.dart

Issue 2990843002: Removed fixed dependencies (Closed)
Patch Set: Created 3 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « packages/analyzer/lib/src/task/model.dart ('k') | packages/analyzer/lib/src/task/options_work_manager.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: packages/analyzer/lib/src/task/options.dart
diff --git a/packages/analyzer/lib/src/task/options.dart b/packages/analyzer/lib/src/task/options.dart
new file mode 100644
index 0000000000000000000000000000000000000000..4047b3b84c244a407b79b3e8e82179c711eed3dd
--- /dev/null
+++ b/packages/analyzer/lib/src/task/options.dart
@@ -0,0 +1,621 @@
+// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
+// 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.
+
+library analyzer.src.task.options;
+
+import 'dart:collection';
+
+import 'package:analyzer/analyzer.dart';
+import 'package:analyzer/plugin/options.dart';
+import 'package:analyzer/source/analysis_options_provider.dart';
+import 'package:analyzer/source/error_processor.dart';
+import 'package:analyzer/src/context/context.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/src/util/yaml.dart';
+import 'package:analyzer/task/general.dart';
+import 'package:analyzer/task/model.dart';
+import 'package:source_span/source_span.dart';
+import 'package:yaml/yaml.dart';
+
+/// The errors produced while parsing an analysis options file.
+///
+/// The list will be empty if there were no errors, but will not be `null`.
+final ListResultDescriptor<AnalysisError> ANALYSIS_OPTIONS_ERRORS =
+ new ListResultDescriptor<AnalysisError>(
+ 'ANALYSIS_OPTIONS_ERRORS', AnalysisError.NO_ERRORS);
+
+/**
+ * The descriptor used to associate error processors with analysis contexts in
+ * configuration data.
+ */
+final ListResultDescriptor<ErrorProcessor> CONFIGURED_ERROR_PROCESSORS =
+ new ListResultDescriptor<ErrorProcessor>(
+ 'configured.errors', const <ErrorProcessor>[]);
+
+final _OptionsProcessor _processor = new _OptionsProcessor();
+
+void applyToAnalysisOptions(
+ AnalysisOptionsImpl options, Map<String, Object> optionMap) {
+ _processor.applyToAnalysisOptions(options, optionMap);
+}
+
+/// Configure this [context] based on configuration details specified in
+/// the given [options]. If [options] is `null`, default values are applied.
+void configureContextOptions(
+ AnalysisContext context, Map<String, Object> options) =>
+ _processor.configure(context, options);
+
+/// `analyzer` analysis options constants.
+class AnalyzerOptions {
+ static const String analyzer = 'analyzer';
+ static const String enableAssertInitializer = 'enableAssertInitializer';
+ static const String enableAsync = 'enableAsync';
+ static const String enableGenericMethods = 'enableGenericMethods';
+ static const String enableInitializingFormalAccess =
+ 'enableInitializingFormalAccess';
+ static const String enableStrictCallChecks = 'enableStrictCallChecks';
+ 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';
+
+ // Strong mode options, see AnalysisOptionsImpl for documentation.
+ static const String implicitCasts = 'implicit-casts';
+ static const String implicitDynamic = 'implicit-dynamic';
+
+ /// Ways to say `ignore`.
+ static const List<String> ignoreSynonyms = const ['ignore', 'false'];
+
+ /// Valid error `severity`s.
+ static final List<String> severities =
+ new List.unmodifiable(severityMap.keys);
+
+ /// 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> topLevel = const [
+ errors,
+ exclude,
+ language,
+ plugins,
+ strong_mode
+ ];
+
+ /// Supported `analyzer` language configuration options.
+ static const List<String> languageOptions = const [
+ enableAssertInitializer,
+ enableAsync,
+ enableGenericMethods,
+ enableStrictCallChecks,
+ enableSuperMixins
+ ];
+}
+
+/// Validates `analyzer` options.
+class AnalyzerOptionsValidator extends CompositeValidator {
+ AnalyzerOptionsValidator()
+ : super([
+ new TopLevelAnalyzerOptionsValidator(),
+ new StrongModeOptionValueValidator(),
+ 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.
+class ErrorFilterOptionValidator extends OptionsValidator {
+ /// Legal values.
+ static final List<String> legalValues =
+ new List.from(AnalyzerOptions.ignoreSynonyms)
+ ..addAll(AnalyzerOptions.includeSynonyms)
+ ..addAll(AnalyzerOptions.severities);
+
+ /// Pretty String listing legal values.
+ static final String legalValueString =
+ StringUtilities.printListOfQuotedNames(legalValues);
+
+ /// Lazily populated set of error codes (hashed for speedy lookup).
+ static HashSet<String> _errorCodes;
+
+ /// Legal error code names.
+ static Set<String> get errorCodes {
+ if (_errorCodes == null) {
+ _errorCodes = new HashSet<String>();
+ // Engine codes.
+ _errorCodes.addAll(ErrorCode.values.map((ErrorCode code) => code.name));
+ }
+ return _errorCodes;
+ }
+
+ @override
+ void validate(ErrorReporter reporter, Map<String, YamlNode> options) {
+ var analyzer = options[AnalyzerOptions.analyzer];
+ if (analyzer is YamlMap) {
+ var filters = analyzer[AnalyzerOptions.errors];
+ if (filters is YamlMap) {
+ String value;
+ filters.nodes.forEach((k, v) {
+ if (k is YamlScalar) {
+ value = toUpperCase(k.value);
+ if (!errorCodes.contains(value)) {
+ reporter.reportErrorForSpan(
+ AnalysisOptionsWarningCode.UNRECOGNIZED_ERROR_CODE,
+ k.span,
+ [k.value?.toString()]);
+ }
+ }
+ if (v is YamlScalar) {
+ value = toLowerCase(v.value);
+ if (!legalValues.contains(value)) {
+ reporter.reportErrorForSpan(
+ AnalysisOptionsWarningCode
+ .UNSUPPORTED_OPTION_WITH_LEGAL_VALUES,
+ v.span,
+ [
+ AnalyzerOptions.errors,
+ v.value?.toString(),
+ legalValueString
+ ]);
+ }
+ }
+ });
+ }
+ }
+ }
+}
+
+/// 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.
+ static const String CONTENT_INPUT_NAME = 'CONTENT_INPUT_NAME';
+
+ /// The task descriptor describing this kind of task.
+ static final TaskDescriptor DESCRIPTOR = new TaskDescriptor(
+ 'GenerateOptionsErrorsTask',
+ createTask,
+ buildInputs,
+ <ResultDescriptor>[ANALYSIS_OPTIONS_ERRORS, LINE_INFO],
+ suitabilityFor: suitabilityFor);
+
+ final AnalysisOptionsProvider optionsProvider = new AnalysisOptionsProvider();
+
+ GenerateOptionsErrorsTask(AnalysisContext context, AnalysisTarget target)
+ : super(context, target);
+
+ @override
+ TaskDescriptor get descriptor => DESCRIPTOR;
+
+ Source get source => target.source;
+
+ @override
+ void internalPerform() {
+ String content = getRequiredInput(CONTENT_INPUT_NAME);
+
+ List<AnalysisError> errors = <AnalysisError>[];
+
+ try {
+ Map<String, YamlNode> options =
+ optionsProvider.getOptionsFromString(content);
+ errors.addAll(_validate(options));
+ } on OptionsFormatException catch (e) {
+ SourceSpan span = e.span;
+ var error = new AnalysisError(source, span.start.column + 1, span.length,
+ AnalysisOptionsErrorCode.PARSE_ERROR, [e.message]);
+ errors.add(error);
+ }
+
+ //
+ // Record outputs.
+ //
+ outputs[ANALYSIS_OPTIONS_ERRORS] = errors;
+ outputs[LINE_INFO] = computeLineInfo(content);
+ }
+
+ List<AnalysisError> _validate(Map<String, YamlNode> options) =>
+ new OptionsFileValidator(source).validate(options);
+
+ /// Return a map from the names of the inputs of this kind of task to the
+ /// task input descriptors describing those inputs for a task with the
+ /// given [target].
+ static Map<String, TaskInput> buildInputs(AnalysisTarget source) =>
+ <String, TaskInput>{CONTENT_INPUT_NAME: CONTENT.of(source)};
+
+ /// Compute [LineInfo] for the given [content].
+ static LineInfo computeLineInfo(String content) {
+ List<int> lineStarts = StringUtilities.computeLineStarts(content);
+ return new LineInfo(lineStarts);
+ }
+
+ /// Create a task based on the given [target] in the given [context].
+ static GenerateOptionsErrorsTask createTask(
+ AnalysisContext context, AnalysisTarget target) =>
+ new GenerateOptionsErrorsTask(context, target);
+
+ /**
+ * Return an indication of how suitable this task is for the given [target].
+ */
+ static TaskSuitability suitabilityFor(AnalysisTarget target) {
+ if (target is Source &&
+ (target.shortName == AnalysisEngine.ANALYSIS_OPTIONS_FILE ||
+ target.shortName == AnalysisEngine.ANALYSIS_OPTIONS_YAML_FILE)) {
+ return TaskSuitability.HIGHEST;
+ }
+ return TaskSuitability.NONE;
+ }
+}
+
+/// 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) {
+ var analyzer = options[AnalyzerOptions.analyzer];
+ if (analyzer is YamlMap) {
+ var 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 {
+ LinterOptionsValidator() : super('linter', const ['rules']);
+}
+
+/// Validates options defined in an analysis options file.
+class OptionsFileValidator {
+ // TODO(pq): move to an extension point.
+ final List<OptionsValidator> _validators = [
+ new AnalyzerOptionsValidator(),
+ new LinterOptionsValidator()
+ ];
+
+ final Source source;
+ OptionsFileValidator(this.source) {
+ _validators.addAll(AnalysisEngine.instance.optionsPlugin.optionsValidators);
+ }
+
+ List<AnalysisError> validate(Map<String, YamlNode> options) {
+ RecordingErrorListener recorder = new RecordingErrorListener();
+ ErrorReporter reporter = new ErrorReporter(recorder, source);
+ _validators.forEach((OptionsValidator v) => v.validate(reporter, options));
+ return recorder.errors;
+ }
+}
+
+/// 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) {
+ 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()
+ : super(AnalyzerOptions.analyzer, AnalyzerOptions.topLevel);
+}
+
+/// Validates top-level options. For example,
+/// plugin:
+/// top-level-option: true
+class TopLevelOptionValidator extends OptionsValidator {
+ final String pluginName;
+ final List<String> supportedOptions;
+ String _valueProposal;
+ AnalysisOptionsWarningCode _warningCode;
+ TopLevelOptionValidator(this.pluginName, this.supportedOptions) {
+ assert(supportedOptions != null && !supportedOptions.isEmpty);
+ if (supportedOptions.length > 1) {
+ _valueProposal = StringUtilities.printListOfQuotedNames(supportedOptions);
+ _warningCode =
+ AnalysisOptionsWarningCode.UNSUPPORTED_OPTION_WITH_LEGAL_VALUES;
+ } else {
+ _valueProposal = "'${supportedOptions.join()}'";
+ _warningCode =
+ AnalysisOptionsWarningCode.UNSUPPORTED_OPTION_WITH_LEGAL_VALUE;
+ }
+ }
+
+ @override
+ void validate(ErrorReporter reporter, Map<String, YamlNode> options) {
+ YamlNode node = options[pluginName];
+ if (node is YamlMap) {
+ node.nodes.forEach((k, v) {
+ if (k is YamlScalar) {
+ if (!supportedOptions.contains(k.value)) {
+ reporter.reportErrorForSpan(
+ _warningCode, k.span, [pluginName, k.value, _valueProposal]);
+ }
+ }
+ //TODO(pq): consider an error if the node is not a Scalar.
+ });
+ }
+ }
+}
+
+/// 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 {
+ static final Map<String, Object> defaults = {'analyzer': {}};
+
+ /**
+ * Apply the options in the given [optionMap] to the given analysis [options].
+ */
+ void applyToAnalysisOptions(
+ AnalysisOptionsImpl options, Map<String, Object> optionMap) {
+ if (optionMap == null) {
+ return;
+ }
+ var analyzer = optionMap[AnalyzerOptions.analyzer];
+ if (analyzer is Map) {
+ // Process strong mode option.
+ var strongMode = analyzer[AnalyzerOptions.strong_mode];
+ _applyStrongOptions(options, strongMode);
+
+ // Process language options.
+ var language = analyzer[AnalyzerOptions.language];
+ _applyLanguageOptions(options, language);
+ }
+ }
+
+ /// Configure [context] based on the given [options] (which can be `null`
+ /// to restore [defaults]).
+ void configure(AnalysisContext context, Map<String, Object> options) {
+ if (options == null) {
+ options = defaults;
+ }
+
+ var analyzer = options[AnalyzerOptions.analyzer];
+ if (analyzer is Map) {
+ // Set strong mode (default is false).
+ var strongMode = analyzer[AnalyzerOptions.strong_mode];
+ setStrongMode(context, strongMode);
+
+ // Set filters.
+ var filters = analyzer[AnalyzerOptions.errors];
+ setProcessors(context, filters);
+
+ // Process language options.
+ var language = analyzer[AnalyzerOptions.language];
+ setLanguageOptions(context, language);
+
+ // Process excludes.
+ var excludes = analyzer[AnalyzerOptions.exclude];
+ setExcludes(context, excludes);
+ }
+ }
+
+ void setExcludes(AnalysisContext context, Object excludes) {
+ if (excludes is YamlList) {
+ List<String> excludeList = toStringList(excludes);
+ if (excludeList != null) {
+ context.setConfigurationData(CONTEXT_EXCLUDES, excludeList);
+ }
+ }
+ }
+
+ void setLanguageOption(
+ AnalysisContext context, Object feature, Object value) {
+ if (feature == AnalyzerOptions.enableAssertInitializer) {
+ if (isTrue(value)) {
+ AnalysisOptionsImpl options =
+ new AnalysisOptionsImpl.from(context.analysisOptions);
+ options.enableAssertInitializer = true;
+ context.analysisOptions = options;
+ }
+ }
+ if (feature == AnalyzerOptions.enableStrictCallChecks) {
+ if (isTrue(value)) {
+ AnalysisOptionsImpl options =
+ new AnalysisOptionsImpl.from(context.analysisOptions);
+ options.enableStrictCallChecks = true;
+ context.analysisOptions = options;
+ }
+ }
+ 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) {
+ if (k is YamlScalar && v is YamlScalar) {
+ String feature = k.value?.toString();
+ setLanguageOption(context, feature, v.value);
+ }
+ });
+ } else if (configs is Map) {
+ configs.forEach((k, v) => setLanguageOption(context, k, v));
+ }
+ }
+
+ void setProcessors(AnalysisContext context, Object codes) {
+ ErrorConfig config = new ErrorConfig(codes);
+ context.setConfigurationData(
+ CONFIGURED_ERROR_PROCESSORS, config.processors);
+ }
+
+ void setStrongMode(AnalysisContext context, Object strongMode) {
+ if (strongMode is Map) {
+ AnalysisOptionsImpl options =
+ new AnalysisOptionsImpl.from(context.analysisOptions);
+ _applyStrongOptions(options, strongMode);
+ context.analysisOptions = options;
+ } else {
+ strongMode = strongMode is bool ? strongMode : false;
+ if (context.analysisOptions.strongMode != strongMode) {
+ AnalysisOptionsImpl options =
+ new AnalysisOptionsImpl.from(context.analysisOptions);
+ options.strongMode = strongMode;
+ context.analysisOptions = options;
+ }
+ }
+ }
+
+ void _applyLanguageOption(
+ AnalysisOptionsImpl options, Object feature, Object value) {
+ bool boolValue = toBool(value);
+ if (boolValue != null) {
+ if (feature == AnalyzerOptions.enableAssertInitializer) {
+ options.enableAssertInitializer = boolValue;
+ } else if (feature == AnalyzerOptions.enableInitializingFormalAccess) {
+ options.enableInitializingFormalAccess = boolValue;
+ } else if (feature == AnalyzerOptions.enableSuperMixins) {
+ options.enableSuperMixins = boolValue;
+ } else if (feature == AnalyzerOptions.enableGenericMethods) {
+ options.enableGenericMethods = boolValue;
+ }
+ }
+ }
+
+ void _applyLanguageOptions(AnalysisOptionsImpl options, Object configs) {
+ if (configs is YamlMap) {
+ configs.nodes.forEach((key, value) {
+ if (key is YamlScalar && value is YamlScalar) {
+ String feature = key.value?.toString();
+ _applyLanguageOption(options, feature, value.value);
+ }
+ });
+ } else if (configs is Map) {
+ configs
+ .forEach((key, value) => _applyLanguageOption(options, key, value));
+ }
+ }
+
+ void _applyStrongModeOption(
+ AnalysisOptionsImpl options, Object feature, Object value) {
+ bool boolValue = toBool(value);
+ if (boolValue != null) {
+ if (feature == AnalyzerOptions.implicitCasts) {
+ options.implicitCasts = boolValue;
+ }
+ if (feature == AnalyzerOptions.implicitDynamic) {
+ options.implicitDynamic = boolValue;
+ }
+ }
+ }
+
+ void _applyStrongOptions(AnalysisOptionsImpl options, Object config) {
+ if (config is YamlMap) {
+ options.strongMode = true;
+ config.nodes.forEach((k, v) {
+ if (k is YamlScalar && v is YamlScalar) {
+ _applyStrongModeOption(options, k.value?.toString(), v.value);
+ }
+ });
+ } else if (config is Map) {
+ options.strongMode = true;
+ config.forEach((k, v) => _applyStrongModeOption(options, k, v));
+ } else {
+ options.strongMode = config is bool ? config : false;
+ }
+ }
+}
« no previous file with comments | « packages/analyzer/lib/src/task/model.dart ('k') | packages/analyzer/lib/src/task/options_work_manager.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698