e29aa0cf3f
Previously, the fields in an AnalysisOptionsImpl were primarily arranged by an extension method, declared externally, called `applyOptions`. It seemed very odd that this wasn't just a method on AnalysisOptionsImpl; the "Impl" class is already private. But it turns out that an AnalysisOptionsImpl is _mostly_ not mutated in practice; the class wants to be immutable, with final fields. In this change, I make AnalysisOptionsImpl mostly immutable, and convert the `applyOptions` extension method, and all of its helper code, into a builder class, AnalysisOptionsBuilder. While we don't use a lot of builders in analyzer packages, this is a much more common and idiomatic pattern for setting up complicated data, multiple values, and then finalizing it all into an object that will not be changed again during it's lifetime. One big benefit of this refactoring is that most fields in AnalysisOptionsImpl are now final: * sourceLanguageConstraint * errorProcessors * excludePatterns * file * strictCasts * strictInference * strictRawTypes * chromeOsManifestChecks * codeStyleOptions * formatterOptions * unignorableNames Whereas before, they _all_ had to be mutable, as `applyOptions`, the primary mechanism for setting up an AnalysisOptionsImpl, had to able to write any field. Other changes: * Flip the instantiation of AnalysisOptionsImpl and CodeStyleOptions; now CodeStyleOptions is instatiated first, and AnalysisOptionsImpl sets itself as `codeStyleOptions.options`. * Remove the private, deprecated, `applyToAnalysisOptions` function. Change-Id: I6595d88aa5721e4f9a8b2b987482f9f43d27efd1 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/390660 Commit-Queue: Samuel Rawlins <srawlins@google.com> Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
182 lines
5.6 KiB
Dart
182 lines
5.6 KiB
Dart
// 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.
|
|
|
|
import 'package:analyzer/error/error.dart';
|
|
import 'package:analyzer/source/error_processor.dart';
|
|
import 'package:analyzer/src/analysis_options/analysis_options_provider.dart';
|
|
import 'package:analyzer/src/error/codes.dart';
|
|
import 'package:analyzer/src/generated/engine.dart';
|
|
import 'package:collection/collection.dart';
|
|
import 'package:test/test.dart';
|
|
import 'package:yaml/yaml.dart';
|
|
|
|
import '../generated/test_support.dart';
|
|
import '../src/util/yaml_test.dart';
|
|
|
|
main() {
|
|
AnalysisError invalid_assignment = AnalysisError.tmp(
|
|
source: TestSource(),
|
|
offset: 0,
|
|
length: 1,
|
|
errorCode: CompileTimeErrorCode.INVALID_ASSIGNMENT,
|
|
arguments: [
|
|
['x'],
|
|
['y'],
|
|
],
|
|
);
|
|
|
|
AnalysisError assignment_of_do_not_store = AnalysisError.tmp(
|
|
source: TestSource(),
|
|
offset: 0,
|
|
length: 1,
|
|
errorCode: WarningCode.ASSIGNMENT_OF_DO_NOT_STORE,
|
|
arguments: [
|
|
['x'],
|
|
],
|
|
);
|
|
|
|
AnalysisError unused_local_variable = AnalysisError.tmp(
|
|
source: TestSource(),
|
|
offset: 0,
|
|
length: 1,
|
|
errorCode: WarningCode.UNUSED_LOCAL_VARIABLE,
|
|
arguments: [
|
|
['x'],
|
|
],
|
|
);
|
|
|
|
AnalysisError use_of_void_result = AnalysisError.tmp(
|
|
source: TestSource(),
|
|
offset: 0,
|
|
length: 1,
|
|
errorCode: CompileTimeErrorCode.USE_OF_VOID_RESULT,
|
|
);
|
|
|
|
// We in-line a lint code here in order to avoid adding a dependency on the
|
|
// linter package.
|
|
AnalysisError annotate_overrides = AnalysisError.tmp(
|
|
source: TestSource(),
|
|
offset: 0,
|
|
length: 1,
|
|
errorCode: LintCode('annotate_overrides', ''),
|
|
);
|
|
|
|
group('ErrorProcessor', () {
|
|
late _TestContext context;
|
|
|
|
setUp(() {
|
|
context = _TestContext();
|
|
});
|
|
|
|
test('configureOptions', () {
|
|
context.configureOptions('''
|
|
analyzer:
|
|
errors:
|
|
invalid_assignment: error # severity ERROR
|
|
assignment_of_do_not_store: false # ignore
|
|
unused_local_variable: true # skipped
|
|
use_of_void_result: unsupported_action # skipped
|
|
''');
|
|
expect(context.getProcessor(invalid_assignment)!.severity,
|
|
ErrorSeverity.ERROR);
|
|
expect(
|
|
context.getProcessor(assignment_of_do_not_store)!.severity, isNull);
|
|
expect(context.getProcessor(unused_local_variable), isNull);
|
|
expect(context.getProcessor(use_of_void_result), isNull);
|
|
});
|
|
|
|
test('does not upgrade other warnings to errors in strong mode', () {
|
|
context.configureOptions('''
|
|
analyzer:
|
|
strong-mode: true
|
|
''');
|
|
expect(context.getProcessor(unused_local_variable), isNull);
|
|
});
|
|
});
|
|
|
|
group('ErrorConfig', () {
|
|
var config = '''
|
|
analyzer:
|
|
errors:
|
|
invalid_assignment: unsupported_action # should be skipped
|
|
assignment_of_do_not_store: false
|
|
unused_local_variable: error
|
|
''';
|
|
|
|
group('processing', () {
|
|
test('yaml map', () {
|
|
var options = AnalysisOptionsProvider().getOptionsFromString(config);
|
|
var errorConfig = ErrorConfig(
|
|
(options['analyzer'] as YamlMap)['errors'] as YamlNode?);
|
|
expect(errorConfig.processors, hasLength(2));
|
|
|
|
// ignore
|
|
var missingReturnProcessor = errorConfig.processors
|
|
.firstWhere((p) => p.appliesTo(assignment_of_do_not_store));
|
|
expect(missingReturnProcessor.severity, isNull);
|
|
|
|
// error
|
|
var unusedLocalProcessor = errorConfig.processors
|
|
.firstWhere((p) => p.appliesTo(unused_local_variable));
|
|
expect(unusedLocalProcessor.severity, ErrorSeverity.ERROR);
|
|
|
|
// skip
|
|
var invalidAssignmentProcessor = errorConfig.processors
|
|
.firstWhereOrNull((p) => p.appliesTo(invalid_assignment));
|
|
expect(invalidAssignmentProcessor, isNull);
|
|
});
|
|
|
|
test('string map', () {
|
|
var options = wrap({
|
|
'invalid_assignment': 'unsupported_action', // should be skipped
|
|
'assignment_of_do_not_store': 'false',
|
|
'unused_local_variable': 'error'
|
|
});
|
|
var errorConfig = ErrorConfig(options);
|
|
expect(errorConfig.processors, hasLength(2));
|
|
|
|
// ignore
|
|
var missingReturnProcessor = errorConfig.processors
|
|
.firstWhere((p) => p.appliesTo(assignment_of_do_not_store));
|
|
expect(missingReturnProcessor.severity, isNull);
|
|
|
|
// error
|
|
var unusedLocalProcessor = errorConfig.processors
|
|
.firstWhere((p) => p.appliesTo(unused_local_variable));
|
|
expect(unusedLocalProcessor.severity, ErrorSeverity.ERROR);
|
|
|
|
// skip
|
|
var invalidAssignmentProcessor = errorConfig.processors
|
|
.firstWhereOrNull((p) => p.appliesTo(invalid_assignment));
|
|
expect(invalidAssignmentProcessor, isNull);
|
|
});
|
|
});
|
|
|
|
test('configure lints', () {
|
|
var options = AnalysisOptionsProvider().getOptionsFromString(
|
|
'analyzer:\n errors:\n annotate_overrides: warning\n');
|
|
var errorConfig =
|
|
ErrorConfig((options['analyzer'] as YamlMap)['errors'] as YamlNode?);
|
|
expect(errorConfig.processors, hasLength(1));
|
|
|
|
ErrorProcessor processor = errorConfig.processors.first;
|
|
expect(processor.appliesTo(annotate_overrides), true);
|
|
expect(processor.severity, ErrorSeverity.WARNING);
|
|
});
|
|
});
|
|
}
|
|
|
|
class _TestContext {
|
|
late AnalysisOptions analysisOptions;
|
|
|
|
void configureOptions(String options) {
|
|
analysisOptions = AnalysisOptionsImpl.fromYaml(
|
|
optionsMap: AnalysisOptionsProvider().getOptionsFromString(options));
|
|
}
|
|
|
|
ErrorProcessor? getProcessor(AnalysisError error) {
|
|
return ErrorProcessor.getProcessor(analysisOptions, error);
|
|
}
|
|
}
|