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

Side by Side Diff: packages/analyzer/lib/src/task/options.dart

Issue 2990843002: Removed fixed dependencies (Closed)
Patch Set: Created 3 years, 4 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 unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file.
4
5 library analyzer.src.task.options;
6
7 import 'dart:collection';
8
9 import 'package:analyzer/analyzer.dart';
10 import 'package:analyzer/plugin/options.dart';
11 import 'package:analyzer/source/analysis_options_provider.dart';
12 import 'package:analyzer/source/error_processor.dart';
13 import 'package:analyzer/src/context/context.dart';
14 import 'package:analyzer/src/generated/engine.dart';
15 import 'package:analyzer/src/generated/java_engine.dart';
16 import 'package:analyzer/src/generated/source.dart';
17 import 'package:analyzer/src/generated/utilities_general.dart';
18 import 'package:analyzer/src/task/general.dart';
19 import 'package:analyzer/src/util/yaml.dart';
20 import 'package:analyzer/task/general.dart';
21 import 'package:analyzer/task/model.dart';
22 import 'package:source_span/source_span.dart';
23 import 'package:yaml/yaml.dart';
24
25 /// The errors produced while parsing an analysis options file.
26 ///
27 /// The list will be empty if there were no errors, but will not be `null`.
28 final ListResultDescriptor<AnalysisError> ANALYSIS_OPTIONS_ERRORS =
29 new ListResultDescriptor<AnalysisError>(
30 'ANALYSIS_OPTIONS_ERRORS', AnalysisError.NO_ERRORS);
31
32 /**
33 * The descriptor used to associate error processors with analysis contexts in
34 * configuration data.
35 */
36 final ListResultDescriptor<ErrorProcessor> CONFIGURED_ERROR_PROCESSORS =
37 new ListResultDescriptor<ErrorProcessor>(
38 'configured.errors', const <ErrorProcessor>[]);
39
40 final _OptionsProcessor _processor = new _OptionsProcessor();
41
42 void applyToAnalysisOptions(
43 AnalysisOptionsImpl options, Map<String, Object> optionMap) {
44 _processor.applyToAnalysisOptions(options, optionMap);
45 }
46
47 /// Configure this [context] based on configuration details specified in
48 /// the given [options]. If [options] is `null`, default values are applied.
49 void configureContextOptions(
50 AnalysisContext context, Map<String, Object> options) =>
51 _processor.configure(context, options);
52
53 /// `analyzer` analysis options constants.
54 class AnalyzerOptions {
55 static const String analyzer = 'analyzer';
56 static const String enableAssertInitializer = 'enableAssertInitializer';
57 static const String enableAsync = 'enableAsync';
58 static const String enableGenericMethods = 'enableGenericMethods';
59 static const String enableInitializingFormalAccess =
60 'enableInitializingFormalAccess';
61 static const String enableStrictCallChecks = 'enableStrictCallChecks';
62 static const String enableSuperMixins = 'enableSuperMixins';
63
64 static const String errors = 'errors';
65 static const String exclude = 'exclude';
66 static const String language = 'language';
67 static const String plugins = 'plugins';
68 static const String strong_mode = 'strong-mode';
69
70 // Strong mode options, see AnalysisOptionsImpl for documentation.
71 static const String implicitCasts = 'implicit-casts';
72 static const String implicitDynamic = 'implicit-dynamic';
73
74 /// Ways to say `ignore`.
75 static const List<String> ignoreSynonyms = const ['ignore', 'false'];
76
77 /// Valid error `severity`s.
78 static final List<String> severities =
79 new List.unmodifiable(severityMap.keys);
80
81 /// Ways to say `include`.
82 static const List<String> includeSynonyms = const ['include', 'true'];
83
84 /// Ways to say `true` or `false`.
85 static const List<String> trueOrFalse = const ['true', 'false'];
86
87 /// Supported top-level `analyzer` options.
88 static const List<String> topLevel = const [
89 errors,
90 exclude,
91 language,
92 plugins,
93 strong_mode
94 ];
95
96 /// Supported `analyzer` language configuration options.
97 static const List<String> languageOptions = const [
98 enableAssertInitializer,
99 enableAsync,
100 enableGenericMethods,
101 enableStrictCallChecks,
102 enableSuperMixins
103 ];
104 }
105
106 /// Validates `analyzer` options.
107 class AnalyzerOptionsValidator extends CompositeValidator {
108 AnalyzerOptionsValidator()
109 : super([
110 new TopLevelAnalyzerOptionsValidator(),
111 new StrongModeOptionValueValidator(),
112 new ErrorFilterOptionValidator(),
113 new LanguageOptionValidator()
114 ]);
115 }
116
117 /// Convenience class for composing validators.
118 class CompositeValidator extends OptionsValidator {
119 final List<OptionsValidator> validators;
120 CompositeValidator(this.validators);
121
122 @override
123 void validate(ErrorReporter reporter, Map<String, YamlNode> options) =>
124 validators.forEach((v) => v.validate(reporter, options));
125 }
126
127 /// Builds error reports with value proposals.
128 class ErrorBuilder {
129 String proposal;
130 AnalysisOptionsWarningCode code;
131
132 /// Create a builder for the given [supportedOptions].
133 ErrorBuilder(List<String> supportedOptions) {
134 assert(supportedOptions != null && !supportedOptions.isEmpty);
135 if (supportedOptions.length > 1) {
136 proposal = StringUtilities.printListOfQuotedNames(supportedOptions);
137 code = pluralProposalCode;
138 } else {
139 proposal = "'${supportedOptions.join()}'";
140 code = singularProposalCode;
141 }
142 }
143 AnalysisOptionsWarningCode get pluralProposalCode =>
144 AnalysisOptionsWarningCode.UNSUPPORTED_OPTION_WITH_LEGAL_VALUES;
145
146 AnalysisOptionsWarningCode get singularProposalCode =>
147 AnalysisOptionsWarningCode.UNSUPPORTED_OPTION_WITH_LEGAL_VALUE;
148
149 /// Report an unsupported [node] value, defined in the given [scopeName].
150 void reportError(ErrorReporter reporter, String scopeName, YamlNode node) {
151 reporter
152 .reportErrorForSpan(code, node.span, [scopeName, node.value, proposal]);
153 }
154 }
155
156 /// Validates `analyzer` error filter options.
157 class ErrorFilterOptionValidator extends OptionsValidator {
158 /// Legal values.
159 static final List<String> legalValues =
160 new List.from(AnalyzerOptions.ignoreSynonyms)
161 ..addAll(AnalyzerOptions.includeSynonyms)
162 ..addAll(AnalyzerOptions.severities);
163
164 /// Pretty String listing legal values.
165 static final String legalValueString =
166 StringUtilities.printListOfQuotedNames(legalValues);
167
168 /// Lazily populated set of error codes (hashed for speedy lookup).
169 static HashSet<String> _errorCodes;
170
171 /// Legal error code names.
172 static Set<String> get errorCodes {
173 if (_errorCodes == null) {
174 _errorCodes = new HashSet<String>();
175 // Engine codes.
176 _errorCodes.addAll(ErrorCode.values.map((ErrorCode code) => code.name));
177 }
178 return _errorCodes;
179 }
180
181 @override
182 void validate(ErrorReporter reporter, Map<String, YamlNode> options) {
183 var analyzer = options[AnalyzerOptions.analyzer];
184 if (analyzer is YamlMap) {
185 var filters = analyzer[AnalyzerOptions.errors];
186 if (filters is YamlMap) {
187 String value;
188 filters.nodes.forEach((k, v) {
189 if (k is YamlScalar) {
190 value = toUpperCase(k.value);
191 if (!errorCodes.contains(value)) {
192 reporter.reportErrorForSpan(
193 AnalysisOptionsWarningCode.UNRECOGNIZED_ERROR_CODE,
194 k.span,
195 [k.value?.toString()]);
196 }
197 }
198 if (v is YamlScalar) {
199 value = toLowerCase(v.value);
200 if (!legalValues.contains(value)) {
201 reporter.reportErrorForSpan(
202 AnalysisOptionsWarningCode
203 .UNSUPPORTED_OPTION_WITH_LEGAL_VALUES,
204 v.span,
205 [
206 AnalyzerOptions.errors,
207 v.value?.toString(),
208 legalValueString
209 ]);
210 }
211 }
212 });
213 }
214 }
215 }
216 }
217
218 /// A task that generates errors for an analysis options file.
219 class GenerateOptionsErrorsTask extends SourceBasedAnalysisTask {
220 /// The name of the input whose value is the content of the file.
221 static const String CONTENT_INPUT_NAME = 'CONTENT_INPUT_NAME';
222
223 /// The task descriptor describing this kind of task.
224 static final TaskDescriptor DESCRIPTOR = new TaskDescriptor(
225 'GenerateOptionsErrorsTask',
226 createTask,
227 buildInputs,
228 <ResultDescriptor>[ANALYSIS_OPTIONS_ERRORS, LINE_INFO],
229 suitabilityFor: suitabilityFor);
230
231 final AnalysisOptionsProvider optionsProvider = new AnalysisOptionsProvider();
232
233 GenerateOptionsErrorsTask(AnalysisContext context, AnalysisTarget target)
234 : super(context, target);
235
236 @override
237 TaskDescriptor get descriptor => DESCRIPTOR;
238
239 Source get source => target.source;
240
241 @override
242 void internalPerform() {
243 String content = getRequiredInput(CONTENT_INPUT_NAME);
244
245 List<AnalysisError> errors = <AnalysisError>[];
246
247 try {
248 Map<String, YamlNode> options =
249 optionsProvider.getOptionsFromString(content);
250 errors.addAll(_validate(options));
251 } on OptionsFormatException catch (e) {
252 SourceSpan span = e.span;
253 var error = new AnalysisError(source, span.start.column + 1, span.length,
254 AnalysisOptionsErrorCode.PARSE_ERROR, [e.message]);
255 errors.add(error);
256 }
257
258 //
259 // Record outputs.
260 //
261 outputs[ANALYSIS_OPTIONS_ERRORS] = errors;
262 outputs[LINE_INFO] = computeLineInfo(content);
263 }
264
265 List<AnalysisError> _validate(Map<String, YamlNode> options) =>
266 new OptionsFileValidator(source).validate(options);
267
268 /// Return a map from the names of the inputs of this kind of task to the
269 /// task input descriptors describing those inputs for a task with the
270 /// given [target].
271 static Map<String, TaskInput> buildInputs(AnalysisTarget source) =>
272 <String, TaskInput>{CONTENT_INPUT_NAME: CONTENT.of(source)};
273
274 /// Compute [LineInfo] for the given [content].
275 static LineInfo computeLineInfo(String content) {
276 List<int> lineStarts = StringUtilities.computeLineStarts(content);
277 return new LineInfo(lineStarts);
278 }
279
280 /// Create a task based on the given [target] in the given [context].
281 static GenerateOptionsErrorsTask createTask(
282 AnalysisContext context, AnalysisTarget target) =>
283 new GenerateOptionsErrorsTask(context, target);
284
285 /**
286 * Return an indication of how suitable this task is for the given [target].
287 */
288 static TaskSuitability suitabilityFor(AnalysisTarget target) {
289 if (target is Source &&
290 (target.shortName == AnalysisEngine.ANALYSIS_OPTIONS_FILE ||
291 target.shortName == AnalysisEngine.ANALYSIS_OPTIONS_YAML_FILE)) {
292 return TaskSuitability.HIGHEST;
293 }
294 return TaskSuitability.NONE;
295 }
296 }
297
298 /// Validates `analyzer` language configuration options.
299 class LanguageOptionValidator extends OptionsValidator {
300 ErrorBuilder builder = new ErrorBuilder(AnalyzerOptions.languageOptions);
301 ErrorBuilder trueOrFalseBuilder = new TrueOrFalseValueErrorBuilder();
302
303 @override
304 void validate(ErrorReporter reporter, Map<String, YamlNode> options) {
305 var analyzer = options[AnalyzerOptions.analyzer];
306 if (analyzer is YamlMap) {
307 var language = analyzer[AnalyzerOptions.language];
308 if (language is YamlMap) {
309 language.nodes.forEach((k, v) {
310 String key, value;
311 bool validKey = false;
312 if (k is YamlScalar) {
313 key = k.value?.toString();
314 if (!AnalyzerOptions.languageOptions.contains(key)) {
315 builder.reportError(reporter, AnalyzerOptions.language, k);
316 } else {
317 // If we have a valid key, go on and check the value.
318 validKey = true;
319 }
320 }
321 if (validKey && v is YamlScalar) {
322 value = toLowerCase(v.value);
323 if (!AnalyzerOptions.trueOrFalse.contains(value)) {
324 trueOrFalseBuilder.reportError(reporter, key, v);
325 }
326 }
327 });
328 }
329 }
330 }
331 }
332
333 /// Validates `linter` top-level options.
334 /// TODO(pq): move into `linter` package and plugin.
335 class LinterOptionsValidator extends TopLevelOptionValidator {
336 LinterOptionsValidator() : super('linter', const ['rules']);
337 }
338
339 /// Validates options defined in an analysis options file.
340 class OptionsFileValidator {
341 // TODO(pq): move to an extension point.
342 final List<OptionsValidator> _validators = [
343 new AnalyzerOptionsValidator(),
344 new LinterOptionsValidator()
345 ];
346
347 final Source source;
348 OptionsFileValidator(this.source) {
349 _validators.addAll(AnalysisEngine.instance.optionsPlugin.optionsValidators);
350 }
351
352 List<AnalysisError> validate(Map<String, YamlNode> options) {
353 RecordingErrorListener recorder = new RecordingErrorListener();
354 ErrorReporter reporter = new ErrorReporter(recorder, source);
355 _validators.forEach((OptionsValidator v) => v.validate(reporter, options));
356 return recorder.errors;
357 }
358 }
359
360 /// Validates `analyzer` strong-mode value configuration options.
361 class StrongModeOptionValueValidator extends OptionsValidator {
362 ErrorBuilder trueOrFalseBuilder = new TrueOrFalseValueErrorBuilder();
363
364 @override
365 void validate(ErrorReporter reporter, Map<String, YamlNode> options) {
366 var analyzer = options[AnalyzerOptions.analyzer];
367 if (analyzer is YamlMap) {
368 var v = analyzer.nodes[AnalyzerOptions.strong_mode];
369 if (v is YamlScalar) {
370 var value = toLowerCase(v.value);
371 if (!AnalyzerOptions.trueOrFalse.contains(value)) {
372 trueOrFalseBuilder.reportError(
373 reporter, AnalyzerOptions.strong_mode, v);
374 }
375 }
376 }
377 }
378 }
379
380 /// Validates `analyzer` top-level options.
381 class TopLevelAnalyzerOptionsValidator extends TopLevelOptionValidator {
382 TopLevelAnalyzerOptionsValidator()
383 : super(AnalyzerOptions.analyzer, AnalyzerOptions.topLevel);
384 }
385
386 /// Validates top-level options. For example,
387 /// plugin:
388 /// top-level-option: true
389 class TopLevelOptionValidator extends OptionsValidator {
390 final String pluginName;
391 final List<String> supportedOptions;
392 String _valueProposal;
393 AnalysisOptionsWarningCode _warningCode;
394 TopLevelOptionValidator(this.pluginName, this.supportedOptions) {
395 assert(supportedOptions != null && !supportedOptions.isEmpty);
396 if (supportedOptions.length > 1) {
397 _valueProposal = StringUtilities.printListOfQuotedNames(supportedOptions);
398 _warningCode =
399 AnalysisOptionsWarningCode.UNSUPPORTED_OPTION_WITH_LEGAL_VALUES;
400 } else {
401 _valueProposal = "'${supportedOptions.join()}'";
402 _warningCode =
403 AnalysisOptionsWarningCode.UNSUPPORTED_OPTION_WITH_LEGAL_VALUE;
404 }
405 }
406
407 @override
408 void validate(ErrorReporter reporter, Map<String, YamlNode> options) {
409 YamlNode node = options[pluginName];
410 if (node is YamlMap) {
411 node.nodes.forEach((k, v) {
412 if (k is YamlScalar) {
413 if (!supportedOptions.contains(k.value)) {
414 reporter.reportErrorForSpan(
415 _warningCode, k.span, [pluginName, k.value, _valueProposal]);
416 }
417 }
418 //TODO(pq): consider an error if the node is not a Scalar.
419 });
420 }
421 }
422 }
423
424 /// An error-builder that knows about `true` and `false` legal values.
425 class TrueOrFalseValueErrorBuilder extends ErrorBuilder {
426 TrueOrFalseValueErrorBuilder() : super(AnalyzerOptions.trueOrFalse);
427 @override
428 AnalysisOptionsWarningCode get pluralProposalCode =>
429 AnalysisOptionsWarningCode.UNSUPPORTED_VALUE;
430 }
431
432 class _OptionsProcessor {
433 static final Map<String, Object> defaults = {'analyzer': {}};
434
435 /**
436 * Apply the options in the given [optionMap] to the given analysis [options].
437 */
438 void applyToAnalysisOptions(
439 AnalysisOptionsImpl options, Map<String, Object> optionMap) {
440 if (optionMap == null) {
441 return;
442 }
443 var analyzer = optionMap[AnalyzerOptions.analyzer];
444 if (analyzer is Map) {
445 // Process strong mode option.
446 var strongMode = analyzer[AnalyzerOptions.strong_mode];
447 _applyStrongOptions(options, strongMode);
448
449 // Process language options.
450 var language = analyzer[AnalyzerOptions.language];
451 _applyLanguageOptions(options, language);
452 }
453 }
454
455 /// Configure [context] based on the given [options] (which can be `null`
456 /// to restore [defaults]).
457 void configure(AnalysisContext context, Map<String, Object> options) {
458 if (options == null) {
459 options = defaults;
460 }
461
462 var analyzer = options[AnalyzerOptions.analyzer];
463 if (analyzer is Map) {
464 // Set strong mode (default is false).
465 var strongMode = analyzer[AnalyzerOptions.strong_mode];
466 setStrongMode(context, strongMode);
467
468 // Set filters.
469 var filters = analyzer[AnalyzerOptions.errors];
470 setProcessors(context, filters);
471
472 // Process language options.
473 var language = analyzer[AnalyzerOptions.language];
474 setLanguageOptions(context, language);
475
476 // Process excludes.
477 var excludes = analyzer[AnalyzerOptions.exclude];
478 setExcludes(context, excludes);
479 }
480 }
481
482 void setExcludes(AnalysisContext context, Object excludes) {
483 if (excludes is YamlList) {
484 List<String> excludeList = toStringList(excludes);
485 if (excludeList != null) {
486 context.setConfigurationData(CONTEXT_EXCLUDES, excludeList);
487 }
488 }
489 }
490
491 void setLanguageOption(
492 AnalysisContext context, Object feature, Object value) {
493 if (feature == AnalyzerOptions.enableAssertInitializer) {
494 if (isTrue(value)) {
495 AnalysisOptionsImpl options =
496 new AnalysisOptionsImpl.from(context.analysisOptions);
497 options.enableAssertInitializer = true;
498 context.analysisOptions = options;
499 }
500 }
501 if (feature == AnalyzerOptions.enableStrictCallChecks) {
502 if (isTrue(value)) {
503 AnalysisOptionsImpl options =
504 new AnalysisOptionsImpl.from(context.analysisOptions);
505 options.enableStrictCallChecks = true;
506 context.analysisOptions = options;
507 }
508 }
509 if (feature == AnalyzerOptions.enableSuperMixins) {
510 if (isTrue(value)) {
511 AnalysisOptionsImpl options =
512 new AnalysisOptionsImpl.from(context.analysisOptions);
513 options.enableSuperMixins = true;
514 context.analysisOptions = options;
515 }
516 }
517 if (feature == AnalyzerOptions.enableGenericMethods) {
518 if (isTrue(value)) {
519 AnalysisOptionsImpl options =
520 new AnalysisOptionsImpl.from(context.analysisOptions);
521 options.enableGenericMethods = true;
522 context.analysisOptions = options;
523 }
524 }
525 }
526
527 void setLanguageOptions(AnalysisContext context, Object configs) {
528 if (configs is YamlMap) {
529 configs.nodes.forEach((k, v) {
530 if (k is YamlScalar && v is YamlScalar) {
531 String feature = k.value?.toString();
532 setLanguageOption(context, feature, v.value);
533 }
534 });
535 } else if (configs is Map) {
536 configs.forEach((k, v) => setLanguageOption(context, k, v));
537 }
538 }
539
540 void setProcessors(AnalysisContext context, Object codes) {
541 ErrorConfig config = new ErrorConfig(codes);
542 context.setConfigurationData(
543 CONFIGURED_ERROR_PROCESSORS, config.processors);
544 }
545
546 void setStrongMode(AnalysisContext context, Object strongMode) {
547 if (strongMode is Map) {
548 AnalysisOptionsImpl options =
549 new AnalysisOptionsImpl.from(context.analysisOptions);
550 _applyStrongOptions(options, strongMode);
551 context.analysisOptions = options;
552 } else {
553 strongMode = strongMode is bool ? strongMode : false;
554 if (context.analysisOptions.strongMode != strongMode) {
555 AnalysisOptionsImpl options =
556 new AnalysisOptionsImpl.from(context.analysisOptions);
557 options.strongMode = strongMode;
558 context.analysisOptions = options;
559 }
560 }
561 }
562
563 void _applyLanguageOption(
564 AnalysisOptionsImpl options, Object feature, Object value) {
565 bool boolValue = toBool(value);
566 if (boolValue != null) {
567 if (feature == AnalyzerOptions.enableAssertInitializer) {
568 options.enableAssertInitializer = boolValue;
569 } else if (feature == AnalyzerOptions.enableInitializingFormalAccess) {
570 options.enableInitializingFormalAccess = boolValue;
571 } else if (feature == AnalyzerOptions.enableSuperMixins) {
572 options.enableSuperMixins = boolValue;
573 } else if (feature == AnalyzerOptions.enableGenericMethods) {
574 options.enableGenericMethods = boolValue;
575 }
576 }
577 }
578
579 void _applyLanguageOptions(AnalysisOptionsImpl options, Object configs) {
580 if (configs is YamlMap) {
581 configs.nodes.forEach((key, value) {
582 if (key is YamlScalar && value is YamlScalar) {
583 String feature = key.value?.toString();
584 _applyLanguageOption(options, feature, value.value);
585 }
586 });
587 } else if (configs is Map) {
588 configs
589 .forEach((key, value) => _applyLanguageOption(options, key, value));
590 }
591 }
592
593 void _applyStrongModeOption(
594 AnalysisOptionsImpl options, Object feature, Object value) {
595 bool boolValue = toBool(value);
596 if (boolValue != null) {
597 if (feature == AnalyzerOptions.implicitCasts) {
598 options.implicitCasts = boolValue;
599 }
600 if (feature == AnalyzerOptions.implicitDynamic) {
601 options.implicitDynamic = boolValue;
602 }
603 }
604 }
605
606 void _applyStrongOptions(AnalysisOptionsImpl options, Object config) {
607 if (config is YamlMap) {
608 options.strongMode = true;
609 config.nodes.forEach((k, v) {
610 if (k is YamlScalar && v is YamlScalar) {
611 _applyStrongModeOption(options, k.value?.toString(), v.value);
612 }
613 });
614 } else if (config is Map) {
615 options.strongMode = true;
616 config.forEach((k, v) => _applyStrongModeOption(options, k, v));
617 } else {
618 options.strongMode = config is bool ? config : false;
619 }
620 }
621 }
OLDNEW
« 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