analyzer: Mark implicit-casts and implicit-dynamic as deprecated

Also add a fix which replaces 'implicit-casts' with 'strict-casts' and
a fix which replaces 'implicit-dynamic' with 'strict-raw-types'.

Fixes #47902

This reverts 5050f31b29

Change-Id: Icd156b0dda78a50ed28272ddef7460018f511cc2
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/276766
Commit-Queue: Samuel Rawlins <srawlins@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
This commit is contained in:
Sam Rawlins 2023-01-03 17:30:28 +00:00 committed by Commit Queue
parent 778e8d13b2
commit 166c54b4d9
17 changed files with 636 additions and 58 deletions

View file

@ -43,6 +43,10 @@ AnalysisOptionsErrorCode.PARSE_ERROR:
AnalysisOptionsHintCode.STRONG_MODE_SETTING_DEPRECATED:
status: needsFix
notes: Fixed.
AnalysisOptionsWarningCode.ANALYSIS_OPTION_DEPRECATED:
status: needsEvaluation
AnalysisOptionsWarningCode.ANALYSIS_OPTION_DEPRECATED_WITH_REPLACEMENT:
status: needsEvaluation
AnalysisOptionsWarningCode.INCLUDE_FILE_NOT_FOUND:
status: noFix
notes: |-

View file

@ -40,6 +40,16 @@ class AnalysisOptionsFixKind {
50,
"Remove '{0}'",
);
static const REPLACE_WITH_STRICT_CASTS = FixKind(
'analysisOptions.fix.replaceWithStrictCasts',
50,
'Replace with the strict-casts analysis mode',
);
static const REPLACE_WITH_STRICT_RAW_TYPES = FixKind(
'analysisOptions.fix.replaceWithStrictRawTypes',
50,
'Replace with the strict-raw-types analysis mode',
);
}
/// The implementation of [DartFixContext].

View file

@ -16,9 +16,12 @@ import 'package:analyzer/src/generated/java_core.dart';
import 'package:analyzer/src/generated/source.dart';
import 'package:analyzer/src/lint/options_rule_validator.dart';
import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart';
import 'package:analyzer_plugin/utilities/change_builder/change_builder_yaml.dart';
import 'package:analyzer_plugin/utilities/change_builder/change_workspace.dart';
import 'package:analyzer_plugin/utilities/fixes/fixes.dart';
import 'package:collection/collection.dart';
import 'package:yaml/yaml.dart';
import 'package:yaml_edit/yaml_edit.dart';
/// The generator used to generate fixes in analysis options files.
class AnalysisOptionsFixGenerator {
@ -64,6 +67,25 @@ class AnalysisOptionsFixGenerator {
// } else
if (errorCode == AnalysisOptionsHintCode.STRONG_MODE_SETTING_DEPRECATED) {
await _addFix_removeSetting(coveringNodePath);
} else if (errorCode ==
AnalysisOptionsWarningCode
.ANALYSIS_OPTION_DEPRECATED_WITH_REPLACEMENT) {
var analyzerMap = options['analyzer'];
if (analyzerMap is! YamlMap) {
return fixes;
}
var strongModeMap = analyzerMap['strong-mode'];
if (strongModeMap is! YamlMap) {
return fixes;
}
if (_isErrorAtMapKey(strongModeMap, 'implicit-casts')) {
await _addFix_replaceWithStrictCasts(
coveringNodePath, analyzerMap, strongModeMap);
} else if (_isErrorAtMapKey(strongModeMap, 'implicit-dynamic')) {
await _addFix_replaceWithStrictRawTypes(
coveringNodePath, analyzerMap, strongModeMap);
}
} else if (errorCode == DEPRECATED_LINT_HINT) {
await _addFix_removeLint(coveringNodePath);
// } else if (errorCode == AnalysisOptionsWarningCode.INCLUDED_FILE_WARNING) {
@ -101,6 +123,52 @@ class AnalysisOptionsFixGenerator {
}
}
/// Replaces `analyzer: strong-mode: implicit-casts: false` with
/// `analyzer: language: strict-casts: true`.
Future<void> _addFix_replaceWithStrictCasts(List<YamlNode> coveringNodePath,
YamlMap analyzerMap, YamlMap strongModeMap) async {
var builder =
ChangeBuilder(workspace: _NonDartChangeWorkspace(resourceProvider));
await builder.addYamlFileEdit(file, (builder) {
_replaceStrongModeEntryWithLanguageEntry(
builder,
coveringNodePath,
analyzerMap,
strongModeMap,
strongModeKey: 'implicit-casts',
languageKey: 'strict-casts',
languageValue: true,
);
});
_addFixFromBuilder(
builder, AnalysisOptionsFixKind.REPLACE_WITH_STRICT_CASTS,
args: [coveringNodePath[0].toString()]);
}
/// Replaces `analyzer: strong-mode: implicit-dynamic: false` with
/// `analyzer: language: strict-raw-types: true`.
Future<void> _addFix_replaceWithStrictRawTypes(
List<YamlNode> coveringNodePath,
YamlMap analyzerMap,
YamlMap strongModeMap) async {
var builder =
ChangeBuilder(workspace: _NonDartChangeWorkspace(resourceProvider));
await builder.addYamlFileEdit(file, (builder) {
_replaceStrongModeEntryWithLanguageEntry(
builder,
coveringNodePath,
analyzerMap,
strongModeMap,
strongModeKey: 'implicit-dynamic',
languageKey: 'strict-raw-types',
languageValue: true,
);
});
_addFixFromBuilder(
builder, AnalysisOptionsFixKind.REPLACE_WITH_STRICT_RAW_TYPES,
args: [coveringNodePath[0].toString()]);
}
/// Add a fix whose edits were built by the [builder] that has the given
/// [kind]. If [args] are provided, they will be used to fill in the message
/// for the fix.
@ -184,6 +252,20 @@ class AnalysisOptionsFixGenerator {
return offset;
}
/// Returns whether the error is located within [map], covering the
/// [YamlScalar] node for [key].
bool _isErrorAtMapKey(YamlMap map, String key) {
var keyNode = map.nodes.keys
.whereType<YamlScalar>()
.firstWhereOrNull((k) => k.value == key);
if (keyNode == null) {
return false;
}
var keyOffset = keyNode.span.start.offset;
var keyLength = keyNode.span.end.offset - keyOffset;
return keyOffset == errorOffset && keyLength == errorLength;
}
SourceRange _lines(int start, int end) {
var startLocation = lineInfo.getLocation(start);
var startOffset = lineInfo.getOffsetOfLine(startLocation.lineNumber - 1);
@ -192,6 +274,53 @@ class AnalysisOptionsFixGenerator {
math.min(endLocation.lineNumber, lineInfo.lineCount - 1));
return SourceRange(startOffset, endOffset - startOffset);
}
/// Replaces a 'strong-mode' entry keyed to [strongModeKey] with a 'language'
/// entry with [languageKey] and [languageValue].
///
/// 'strong-mode' and 'language' are each maps which can be found under the
/// top-level 'analyzer' map. 'strong-mode' (given as [strongModeMap]) must
/// already be present under the 'analyzer' map (given as [analyzerMap]).
void _replaceStrongModeEntryWithLanguageEntry(
YamlFileEditBuilder builder,
List<YamlNode> coveringNodePath,
YamlMap analyzerMap,
YamlMap strongModeMap, {
required String strongModeKey,
required String languageKey,
required Object? languageValue,
}) {
var yamlEditor = YamlEditor(content);
// If 'language' does not exist yet under 'analyzer', create it.
if (analyzerMap['language'] == null) {
yamlEditor.update(['analyzer', 'language'], {languageKey: languageValue});
} else {
yamlEditor.update(['analyzer', 'language', languageKey], languageValue);
}
var languageEdit = yamlEditor.edits.single;
builder.addSimpleReplacement(
SourceRange(languageEdit.offset, languageEdit.length),
languageEdit.replacement);
// If `strongModeKey` is the only entry under 'strong-mode', then remove
// the entire 'strong-mode' entry.
if (strongModeMap.length == 1) {
yamlEditor.remove(['analyzer', 'strong-mode']);
} else {
yamlEditor.remove(['analyzer', 'strong-mode', strongModeKey]);
}
var strongModeEdit = yamlEditor.edits[1];
int strongModeEditOffset;
if (strongModeEdit.offset > languageEdit.offset) {
strongModeEditOffset = strongModeEdit.offset -
(languageEdit.replacement.length - languageEdit.length);
} else {
strongModeEditOffset = strongModeEdit.offset;
}
builder.addSimpleReplacement(
SourceRange(strongModeEditOffset, strongModeEdit.length),
strongModeEdit.replacement);
}
}
class _NonDartChangeWorkspace implements ChangeWorkspace {

View file

@ -24,6 +24,7 @@ dependencies:
test: any
watcher: any
yaml: any
yaml_edit: any
# Use 'any' constraints here; we get our versions from the DEPS file.
dev_dependencies:

View file

@ -65,6 +65,10 @@ class AbstractContextTest with ResourceProviderMixin {
Future<AnalysisSession> get session => sessionFor(testPackageRootPath);
/// The path for `analysis_options.yaml` in [testPackageRootPath].
String get testAnalysisOptionsPath =>
convertPath('$testPackageRootPath/analysis_options.yaml');
String? get testPackageLanguageVersion => latestLanguageVersion;
String get testPackageLibPath => '$testPackageRootPath/lib';

View file

@ -58,9 +58,15 @@ class AnalysisOptionsFileConfig {
buffer.writeln(' strict-casts: $strictCasts');
buffer.writeln(' strict-inference: $strictInference');
buffer.writeln(' strict-raw-types: $strictRawTypes');
buffer.writeln(' strong-mode:');
buffer.writeln(' implicit-casts: $implicitCasts');
buffer.writeln(' implicit-dynamic: $implicitDynamic');
if (!implicitCasts || !implicitDynamic) {
buffer.writeln(' strong-mode:');
if (!implicitCasts) {
buffer.writeln(' implicit-casts: $implicitCasts');
}
if (!implicitDynamic) {
buffer.writeln(' implicit-dynamic: $implicitDynamic');
}
}
buffer.writeln('linter:');
buffer.writeln(' rules:');

View file

@ -271,7 +271,7 @@ class BasicCompletionTest2 extends AbstractCompletionDriverTest
}
mixin BasicCompletionTestCases on AbstractCompletionDriverTest {
/// Duplicates (and potentially replaces DeprecatedMemberRelevanceTest).
/// Duplicates (and potentially replaces) [DeprecatedMemberRelevanceTest].
Future<void> test_deprecated_member_relevance() async {
await addTestFile('''
class A {

View file

@ -0,0 +1,163 @@
// Copyright (c) 2022, 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:test_reflective_loader/test_reflective_loader.dart';
import 'test_support.dart';
void main() {
defineReflectiveSuite(() {
defineReflectiveTests(ReplaceWithStrictCastsTest);
});
}
@reflectiveTest
class ReplaceWithStrictCastsTest extends AnalysisOptionsFixTest {
Future<void> test_hasLanguage() async {
await assertHasFix('''
analyzer:
language:
strict-inference: true
strong-mode:
implicit-casts: false
''', '''
analyzer:
language:
strict-casts: true
strict-inference: true
''');
}
Future<void> test_hasLanguage_isAfter() async {
await assertHasFix('''
analyzer:
strong-mode:
implicit-casts: false
language:
strict-inference: true
''', '''
analyzer:
language:
strict-casts: true
strict-inference: true
''');
}
Future<void> test_hasStrictCastsFalse() async {
await assertHasFix('''
analyzer:
language:
strict-casts: false
strong-mode:
implicit-casts: false
''', '''
analyzer:
language:
strict-casts: true
''');
}
Future<void> test_hasStrictCastsTrue() async {
await assertHasFix('''
analyzer:
language:
strict-casts: true
strong-mode:
implicit-casts: false
''', '''
analyzer:
language:
strict-casts: true
''');
}
Future<void> test_noLanguage() async {
await assertHasFix('''
analyzer:
strong-mode:
implicit-casts: false
''', '''
analyzer:
language:
strict-casts: true
''');
}
Future<void> test_noLanguage_analyzerHasOtherEntries() async {
await assertHasFix('''
analyzer:
errors:
unused_import: ignore
strong-mode:
implicit-casts: false
''', '''
analyzer:
errors:
unused_import: ignore
language:
strict-casts: true
''');
}
Future<void> test_noLanguage_analyzerHasOtherEntriesAfter() async {
await assertHasFix('''
analyzer:
strong-mode:
implicit-casts: false
errors:
unused_import: ignore
''', '''
analyzer:
errors:
unused_import: ignore
language:
strict-casts: true
''');
}
Future<void> test_noLanguage_hasOtherStrongModeEntry() async {
await assertHasFix('''
analyzer:
strong-mode:
implicit-casts: false
implicit-dynamic: false
''', '''
analyzer:
language:
strict-casts: true
strong-mode:
implicit-dynamic: false
''', errorFilter: (error) => error.message.contains('implicit-casts'));
}
Future<void> test_noLanguage_implicitCastsHasComment() async {
await assertHasFix('''
analyzer:
strong-mode:
# No implicit casts
implicit-casts: false
''', '''
analyzer:
language:
strict-casts: true
''');
}
Future<void> test_noLanguage_strongModeHasComment() async {
// TODO(srawlins): This is unfortunate; it would be better to remove the
// comment. But we leave this assertion as is to show at least the file is
// not corrupted.
await assertHasFix('''
analyzer:
# Strong mode
strong-mode:
implicit-casts: false
''', '''
analyzer:
# Strong mode
language:
strict-casts: true
''');
}
}

View file

@ -0,0 +1,163 @@
// Copyright (c) 2022, 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:test_reflective_loader/test_reflective_loader.dart';
import 'test_support.dart';
void main() {
defineReflectiveSuite(() {
defineReflectiveTests(ReplaceWithStrictRawTypesTest);
});
}
@reflectiveTest
class ReplaceWithStrictRawTypesTest extends AnalysisOptionsFixTest {
Future<void> test_hasLanguage() async {
await assertHasFix('''
analyzer:
language:
strict-inference: true
strong-mode:
implicit-dynamic: false
''', '''
analyzer:
language:
strict-inference: true
strict-raw-types: true
''');
}
Future<void> test_hasLanguage_isAfter() async {
await assertHasFix('''
analyzer:
strong-mode:
implicit-dynamic: false
language:
strict-inference: true
''', '''
analyzer:
language:
strict-inference: true
strict-raw-types: true
''');
}
Future<void> test_hasStrictRawTypesFalse() async {
await assertHasFix('''
analyzer:
language:
strict-raw-types: false
strong-mode:
implicit-dynamic: false
''', '''
analyzer:
language:
strict-raw-types: true
''');
}
Future<void> test_hasStrictRawTypesTrue() async {
await assertHasFix('''
analyzer:
language:
strict-raw-types: true
strong-mode:
implicit-dynamic: false
''', '''
analyzer:
language:
strict-raw-types: true
''');
}
Future<void> test_noLanguage() async {
await assertHasFix('''
analyzer:
strong-mode:
implicit-dynamic: false
''', '''
analyzer:
language:
strict-raw-types: true
''');
}
Future<void> test_noLanguage_analyzerHasOtherEntries() async {
await assertHasFix('''
analyzer:
errors:
unused_import: ignore
strong-mode:
implicit-dynamic: false
''', '''
analyzer:
errors:
unused_import: ignore
language:
strict-raw-types: true
''');
}
Future<void> test_noLanguage_analyzerHasOtherEntriesAfter() async {
await assertHasFix('''
analyzer:
strong-mode:
implicit-dynamic: false
errors:
unused_import: ignore
''', '''
analyzer:
errors:
unused_import: ignore
language:
strict-raw-types: true
''');
}
Future<void> test_noLanguage_hasOtherStrongModeEntry() async {
await assertHasFix('''
analyzer:
strong-mode:
implicit-casts: false
implicit-dynamic: false
''', '''
analyzer:
language:
strict-raw-types: true
strong-mode:
implicit-casts: false
''', errorFilter: (error) => error.message.contains('implicit-dynamic'));
}
Future<void> test_noLanguage_implicitDynamicHasComment() async {
await assertHasFix('''
analyzer:
strong-mode:
# No implicit dynamic
implicit-dynamic: false
''', '''
analyzer:
language:
strict-raw-types: true
''');
}
Future<void> test_noLanguage_strongModeHasComment() async {
// TODO(srawlins): This is unfortunate; it would be better to remove the
// comment. But we leave this assertion as is to show at least the file is
// not corrupted.
await assertHasFix('''
analyzer:
# Strong mode
strong-mode:
implicit-dynamic: false
''', '''
analyzer:
# Strong mode
language:
strict-raw-types: true
''');
}
}

View file

@ -6,10 +6,15 @@ import 'package:test_reflective_loader/test_reflective_loader.dart';
import 'remove_lint_test.dart' as remove_lint;
import 'remove_setting_test.dart' as remove_setting;
import 'replace_with_strict_casts_test.dart' as replace_with_strict_casts;
import 'replace_with_strict_raw_types_test.dart'
as replace_with_strict_raw_types;
void main() {
defineReflectiveSuite(() {
remove_lint.main();
remove_setting.main();
replace_with_strict_casts.main();
replace_with_strict_raw_types.main();
});
}

View file

@ -3,12 +3,13 @@
// BSD-style license that can be found in the LICENSE file.
import 'package:analysis_server/plugin/edit/fix/fix_core.dart';
import 'package:analysis_server/src/protocol_server.dart' show SourceEdit;
import 'package:analysis_server/src/services/correction/fix/analysis_options/fix_generator.dart';
import 'package:analyzer/error/error.dart';
import 'package:analyzer/src/generated/source.dart';
import 'package:analyzer/src/task/options.dart';
import 'package:analyzer/src/test_utilities/resource_provider_mixin.dart';
import 'package:analyzer_plugin/protocol/protocol_common.dart';
import 'package:analyzer_plugin/protocol/protocol_common.dart'
hide AnalysisError;
import 'package:test/test.dart';
import 'package:yaml/yaml.dart';
@ -16,8 +17,11 @@ import 'package:yaml/yaml.dart';
/// errors in Dart files.
class AnalysisOptionsFixTest with ResourceProviderMixin {
Future<void> assertHasFix(
String initialContent, String expectedContent) async {
var fixes = await _getFixes(initialContent);
String initialContent,
String expectedContent, {
bool Function(AnalysisError)? errorFilter,
}) async {
var fixes = await _getFixes(initialContent, errorFilter: errorFilter);
expect(fixes, hasLength(1));
var fileEdits = fixes[0].change.edits;
expect(fileEdits, hasLength(1));
@ -32,8 +36,11 @@ class AnalysisOptionsFixTest with ResourceProviderMixin {
expect(fixes, hasLength(0));
}
Future<List<Fix>> _getFixes(String content) {
var optionsFile = getFile('/analysis_options.yaml');
Future<List<Fix>> _getFixes(
String content, {
bool Function(AnalysisError)? errorFilter,
}) {
var optionsFile = newFile('/analysis_options.yaml', content);
var sourceFactory = SourceFactory([]);
var errors = analyzeAnalysisOptions(
optionsFile.createSource(),
@ -41,6 +48,12 @@ class AnalysisOptionsFixTest with ResourceProviderMixin {
sourceFactory,
'/',
);
if (errorFilter != null) {
if (errors.length == 1) {
fail('Unnecessary error filter');
}
errors = errors.where(errorFilter).toList();
}
expect(errors, hasLength(1));
var error = errors[0];
var options = _parseYaml(content);

View file

@ -90,6 +90,30 @@ class AnalysisOptionsHintCode extends ErrorCode {
}
class AnalysisOptionsWarningCode extends ErrorCode {
/// An error code indicating that the given option is deprecated.
///
/// Parameters:
/// 0: the option name
///
static const AnalysisOptionsWarningCode ANALYSIS_OPTION_DEPRECATED =
AnalysisOptionsWarningCode(
'ANALYSIS_OPTION_DEPRECATED',
"The option '{0}' is no longer supported.",
);
/// An error code indicating that the given option is deprecated.
///
/// Parameters:
/// 0: the option name
/// 1: the replacement option name
static const AnalysisOptionsWarningCode
ANALYSIS_OPTION_DEPRECATED_WITH_REPLACEMENT = AnalysisOptionsWarningCode(
'ANALYSIS_OPTION_DEPRECATED',
"The option '{0}' is no longer supported.",
correctionMessage: "Try using the new '{1}' option.",
uniqueName: 'ANALYSIS_OPTION_DEPRECATED_WITH_REPLACEMENT',
);
/// An error code indicating a specified include file has a warning.
///
/// Parameters:

View file

@ -23,6 +23,8 @@ const List<ErrorCode> errorCodeValues = [
AnalysisOptionsErrorCode.INCLUDED_FILE_PARSE_ERROR,
AnalysisOptionsErrorCode.PARSE_ERROR,
AnalysisOptionsHintCode.STRONG_MODE_SETTING_DEPRECATED,
AnalysisOptionsWarningCode.ANALYSIS_OPTION_DEPRECATED,
AnalysisOptionsWarningCode.ANALYSIS_OPTION_DEPRECATED_WITH_REPLACEMENT,
AnalysisOptionsWarningCode.INCLUDED_FILE_WARNING,
AnalysisOptionsWarningCode.INCLUDE_FILE_NOT_FOUND,
AnalysisOptionsWarningCode.INVALID_OPTION,

View file

@ -625,38 +625,48 @@ class StrongModeOptionValueValidator extends OptionsValidator {
void validate(ErrorReporter reporter, YamlMap options) {
var analyzer = options.valueAt(AnalyzerOptions.analyzer);
if (analyzer is YamlMap) {
var v = analyzer.valueAt(AnalyzerOptions.strongMode);
if (v is YamlScalar) {
var value = toLowerCase(v.value);
if (!AnalyzerOptions.trueOrFalse.contains(value)) {
var strongModeNode = analyzer.valueAt(AnalyzerOptions.strongMode);
if (strongModeNode is YamlScalar) {
return _validateStrongModeAsScalar(reporter, strongModeNode);
} else if (strongModeNode is YamlMap) {
return _validateStrongModeAsMap(reporter, strongModeNode);
} else if (strongModeNode != null) {
reporter.reportErrorForSpan(
AnalysisOptionsWarningCode.INVALID_SECTION_FORMAT,
strongModeNode.span,
[AnalyzerOptions.strongMode]);
}
}
}
void _validateStrongModeAsMap(
ErrorReporter reporter, YamlMap strongModeNode) {
strongModeNode.nodes.forEach((k, v) {
if (k is YamlScalar) {
var key = k.value?.toString();
if (!AnalyzerOptions.strongModeOptions.contains(key)) {
_builder.reportError(reporter, AnalyzerOptions.strongMode, k);
} else if (key == AnalyzerOptions.declarationCasts) {
reporter.reportErrorForSpan(
AnalysisOptionsWarningCode.UNSUPPORTED_VALUE, v.span, [
AnalyzerOptions.strongMode,
v.value,
AnalyzerOptions.trueOrFalseProposal
]);
} else if (value == 'false') {
AnalysisOptionsWarningCode.ANALYSIS_OPTION_DEPRECATED,
k.span,
[AnalyzerOptions.declarationCasts]);
} else if (key == AnalyzerOptions.implicitCasts) {
reporter.reportErrorForSpan(
AnalysisOptionsWarningCode.SPEC_MODE_REMOVED, v.span);
} else if (value == 'true') {
AnalysisOptionsWarningCode
.ANALYSIS_OPTION_DEPRECATED_WITH_REPLACEMENT,
k.span,
[AnalyzerOptions.implicitCasts, 'strict-casts']);
} else if (key == AnalyzerOptions.implicitDynamic) {
reporter.reportErrorForSpan(
AnalysisOptionsHintCode.STRONG_MODE_SETTING_DEPRECATED, v.span);
}
} else if (v is YamlMap) {
v.nodes.forEach((k, v) {
String? key, value;
bool validKey = false;
if (k is YamlScalar) {
key = k.value?.toString();
if (!AnalyzerOptions.strongModeOptions.contains(key)) {
_builder.reportError(reporter, AnalyzerOptions.strongMode, k);
} else if (key != AnalyzerOptions.declarationCasts) {
// If we have a valid key, go on and check the value.
validKey = true;
}
}
if (validKey && v is YamlScalar) {
value = toLowerCase(v.value);
AnalysisOptionsWarningCode
.ANALYSIS_OPTION_DEPRECATED_WITH_REPLACEMENT,
k.span,
[AnalyzerOptions.implicitDynamic, 'strict-raw-types']);
} else {
// The key is valid.
if (v is YamlScalar) {
var value = toLowerCase(v.value);
if (!AnalyzerOptions.trueOrFalse.contains(value)) {
reporter.reportErrorForSpan(
AnalysisOptionsWarningCode.UNSUPPORTED_VALUE,
@ -664,13 +674,28 @@ class StrongModeOptionValueValidator extends OptionsValidator {
[key!, v.value, AnalyzerOptions.trueOrFalseProposal]);
}
}
});
} else if (v != null) {
reporter.reportErrorForSpan(
AnalysisOptionsWarningCode.INVALID_SECTION_FORMAT,
v.span,
[AnalyzerOptions.enableExperiment]);
}
}
});
}
void _validateStrongModeAsScalar(
ErrorReporter reporter, YamlScalar strongModeNode) {
var stringValue = toLowerCase(strongModeNode.value);
if (!AnalyzerOptions.trueOrFalse.contains(stringValue)) {
reporter.reportErrorForSpan(
AnalysisOptionsWarningCode.UNSUPPORTED_VALUE, strongModeNode.span, [
AnalyzerOptions.strongMode,
strongModeNode.value,
AnalyzerOptions.trueOrFalseProposal
]);
} else if (stringValue == 'false') {
reporter.reportErrorForSpan(
AnalysisOptionsWarningCode.SPEC_MODE_REMOVED, strongModeNode.span);
} else if (stringValue == 'true') {
reporter.reportErrorForSpan(
AnalysisOptionsHintCode.STRONG_MODE_SETTING_DEPRECATED,
strongModeNode.span);
}
}
}

View file

@ -59,6 +59,23 @@ AnalysisOptionsHintCode:
correctionMessage: It is no longer necessary to explicitly enable strong mode.
comment: "An error code indicating that strong-mode: true is deprecated."
AnalysisOptionsWarningCode:
ANALYSIS_OPTION_DEPRECATED:
problemMessage: "The option '{0}' is no longer supported."
comment: |
An error code indicating that the given option is deprecated.
Parameters:
0: the option name
ANALYSIS_OPTION_DEPRECATED_WITH_REPLACEMENT:
sharedName: ANALYSIS_OPTION_DEPRECATED
problemMessage: "The option '{0}' is no longer supported."
correctionMessage: "Try using the new '{1}' option."
comment: |-
An error code indicating that the given option is deprecated.
Parameters:
0: the option name
1: the replacement option name
INCLUDED_FILE_WARNING:
problemMessage: "Warning in the included options file {0}({1}..{2}): {3}"
comment: |-

View file

@ -452,6 +452,30 @@ analyzer:
''', [AnalysisOptionsHintCode.STRONG_MODE_SETTING_DEPRECATED]);
}
test_analyzer_strong_mode_deprecated_key() {
validate('''
analyzer:
strong-mode:
declaration-casts: false
''', [AnalysisOptionsWarningCode.ANALYSIS_OPTION_DEPRECATED]);
}
test_analyzer_strong_mode_deprecated_key_implicit_casts() {
validate('''
analyzer:
strong-mode:
implicit-casts: false
''', [AnalysisOptionsWarningCode.ANALYSIS_OPTION_DEPRECATED_WITH_REPLACEMENT]);
}
test_analyzer_strong_mode_deprecated_key_implicit_dynamic() {
validate('''
analyzer:
strong-mode:
implicit-dynamic: false
''', [AnalysisOptionsWarningCode.ANALYSIS_OPTION_DEPRECATED_WITH_REPLACEMENT]);
}
test_analyzer_strong_mode_error_code_supported() {
validate('''
analyzer:
@ -483,14 +507,6 @@ analyzer:
''', [AnalysisOptionsWarningCode.UNSUPPORTED_OPTION_WITH_LEGAL_VALUES]);
}
test_analyzer_strong_mode_unsupported_value() {
validate('''
analyzer:
strong-mode:
implicit-dynamic: foo
''', [AnalysisOptionsWarningCode.UNSUPPORTED_VALUE]);
}
test_analyzer_supported_exclude() {
validate('''
analyzer:

View file

@ -188,11 +188,7 @@ class ChangeBuilderImpl implements ChangeBuilder {
this,
path,
loadYamlDocument(
workspace
.getSession(path)!
.resourceProvider
.getFile(path)
.readAsStringSync(),
workspace.resourceProvider.getFile(path).readAsStringSync(),
recover: true),
0);
_yamlFileEditBuilders[path] = builder;