blob: 11a57f18f53da4a18cb5675c1c098780f6cf6cff [file] [log] [blame]
// Copyright (c) 2018, 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/analysis_rule/rule_state.dart';
import 'package:analyzer/error/error.dart';
import 'package:analyzer/error/listener.dart';
import 'package:analyzer/src/analysis_options/analysis_options_provider.dart';
import 'package:analyzer/src/analysis_options/error/option_codes.dart';
import 'package:analyzer/src/lint/linter.dart';
import 'package:analyzer/src/lint/options_rule_validator.dart';
import 'package:analyzer/src/string_source.dart';
import 'package:analyzer/src/test_utilities/test_code_format.dart';
import 'package:pub_semver/pub_semver.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
import 'package:yaml/yaml.dart';
import '../../generated/test_support.dart';
import '../diagnostics/analysis_options/analysis_options_test_support.dart';
main() {
defineReflectiveSuite(() {
defineReflectiveTests(OptionsRuleValidatorIncludedFileTest);
defineReflectiveTests(OptionsRuleValidatorTest);
defineReflectiveTests(OptionsRuleValidatorValueTest);
});
}
class DeprecatedLint extends TestLintRule {
DeprecatedLint()
: super(name: 'deprecated_lint', state: RuleState.deprecated());
}
class DeprecatedLintWithReplacement extends TestLintRule {
DeprecatedLintWithReplacement()
: super(
name: 'deprecated_lint_with_replacement',
state: RuleState.deprecated(replacedBy: 'replacing_lint'),
);
}
class DeprecatedSince3Lint extends TestLintRule {
DeprecatedSince3Lint()
: super(
name: 'deprecated_since_3_lint',
state: RuleState.deprecated(since: dart3),
);
}
@reflectiveTest
class OptionsRuleValidatorIncludedFileTest extends AbstractAnalysisOptionsTest
with OptionsRuleValidatorTestMixin {
void test_compatible_multiple_include() {
newFile('/included1.yaml', '''
linter:
rules:
rule_pos: true
''');
newFile('/included2.yaml', '''
linter:
rules:
rule_pos: true
''');
assertNoErrors('''
include:
- included1.yaml
- included2.yaml
''');
}
Future<void> test_deprecated_rule_inInclude_ok() async {
newFile('/included.yaml', '''
linter:
rules:
- deprecated_lint
''');
assertNoErrors('''
include: included.yaml
''');
}
Future<void> test_incompatible_multiple_include() async {
var included1Code = TestCode.parse('''
linter:
rules:
[!rule_neg!]: true
''');
var included1 = newFile('/included1.yaml', included1Code.code);
var included2Code = TestCode.parse('''
linter:
rules:
[!rule_pos!]: true
''');
var included2 = newFile('/included2.yaml', included2Code.code);
var testCode = TestCode.parse('''
include:
- included1.yaml
- [!included2.yaml!]
''');
await assertErrorsInCode(testCode.code, [
ExpectedError(
AnalysisOptionsWarningCode.incompatibleLintIncluded,
testCode.range.sourceRange.offset,
testCode.range.sourceRange.length,
expectedContextMessages: [
ExpectedContextMessage(
included2,
included2Code.range.sourceRange.offset,
included2Code.range.sourceRange.length,
),
ExpectedContextMessage(
included1,
included1Code.range.sourceRange.offset,
included1Code.range.sourceRange.length,
),
],
),
]);
}
Future<void> test_incompatible_multiple_include_disabled() async {
newFile('/included1.yaml', '''
linter:
rules:
rule_neg: true
''');
newFile('/included2.yaml', '''
linter:
rules:
rule_pos: true
''');
await assertNoErrorsInCode('''
include:
- included1.yaml
- included2.yaml
linter:
rules:
rule_neg: false
''');
}
Future<void> test_incompatible_multiple_include_list() async {
var included1Code = TestCode.parse('''
linter:
rules:
- [!rule_neg!]
''');
var included1 = newFile('/included1.yaml', included1Code.code);
var included2Code = TestCode.parse('''
linter:
rules:
- [!rule_pos!]
''');
var included2 = newFile('/included2.yaml', included2Code.code);
var testCode = TestCode.parse('''
include:
- included1.yaml
- [!included2.yaml!]
''');
await assertErrorsInCode(testCode.code, [
ExpectedError(
AnalysisOptionsWarningCode.incompatibleLintIncluded,
testCode.range.sourceRange.offset,
testCode.range.sourceRange.length,
expectedContextMessages: [
ExpectedContextMessage(
included2,
included2Code.range.sourceRange.offset,
included2Code.range.sourceRange.length,
),
ExpectedContextMessage(
included1,
included1Code.range.sourceRange.offset,
included1Code.range.sourceRange.length,
),
],
),
]);
}
void test_incompatible_noTrigger_invalidMap() {
newFile('/included.yaml', '''
linter:
rules:
rule_neg: true
''');
assertNoErrors('''
include: included.yaml
linter:
rules:
rule_neg: true
rule_pos:
''');
}
Future<void> test_incompatible_rule_map_include() async {
var includedCode = TestCode.parse('''
linter:
rules:
[!rule_neg!]: true
''');
var included = newFile('/included.yaml', includedCode.code);
var testCode = TestCode.parse('''
include: included.yaml
linter:
rules:
[!rule_pos!]: true
''');
await assertErrorsInCode(testCode.code, [
ExpectedError(
AnalysisOptionsWarningCode.incompatibleLintFiles,
testCode.range.sourceRange.offset,
testCode.range.sourceRange.length,
expectedContextMessages: [
ExpectedContextMessage(
included,
includedCode.range.sourceRange.offset,
includedCode.range.sourceRange.length,
),
],
),
]);
}
Future<void> test_incompatible_rule_map_include_disabled() async {
newFile('/included.yaml', '''
linter:
rules:
rule_neg: true
''');
await assertNoErrorsInCode('''
include: included.yaml
linter:
rules:
rule_pos: true
rule_neg: false
''');
}
void test_incompatible_unsuportedValue_invalidMap() {
newFile('/included.yaml', '''
linter:
rules:
rule_neg: true
''');
assertErrors(
'''
include: included.yaml
linter:
rules:
rule_pos: invalid_value
''',
[AnalysisOptionsWarningCode.unsupportedValue],
);
}
Future<void> test_removed_rule_inInclude_ok() async {
newFile('/included.yaml', '''
linter:
rules:
- removed_in_2_12_lint
''');
assertNoErrors('''
include: included.yaml
''');
}
}
@reflectiveTest
class OptionsRuleValidatorTest extends AbstractAnalysisOptionsTest
with OptionsRuleValidatorTestMixin {
void test_deprecated_rule() {
assertErrors(
'''
linter:
rules:
- deprecated_lint_with_replacement
''',
[AnalysisOptionsWarningCode.deprecatedLintWithReplacement],
);
}
void test_deprecated_rule_map() {
assertErrors(
'''
linter:
rules:
deprecated_lint: false
''',
[AnalysisOptionsWarningCode.deprecatedLint],
);
}
void test_deprecated_rule_withReplacement() {
assertErrors(
'''
linter:
rules:
- deprecated_lint
''',
[AnalysisOptionsWarningCode.deprecatedLint],
);
}
void test_deprecated_rule_withSince_inCurrentSdk() {
assertErrors(
'''
linter:
rules:
- deprecated_since_3_lint
''',
[AnalysisOptionsWarningCode.deprecatedLint],
sdk: dart3,
);
}
void test_deprecated_rule_withSince_notInCurrentSdk() {
assertNoErrors('''
linter:
rules:
- deprecated_since_3_lint
''', sdk: Version(2, 17, 0));
}
void test_deprecated_rule_withSince_unknownSdk() {
assertNoErrors('''
linter:
rules:
- deprecated_since_3_lint
''');
}
void test_duplicated_rule() {
assertErrors(
'''
linter:
rules:
- stable_lint
- stable_lint
''',
[AnalysisOptionsWarningCode.duplicateRule],
);
}
Future<void> test_incompatible_rule() async {
var testCode = TestCode.parse('''
linter:
rules:
- /*[0*/rule_pos/*0]*/
- /*[1*/rule_neg/*1]*/
''');
await assertErrorsInCode(testCode.code, [
ExpectedError(
AnalysisOptionsWarningCode.incompatibleLint,
testCode.ranges.last.sourceRange.offset,
testCode.ranges.last.sourceRange.length,
expectedContextMessages: [
ExpectedContextMessage(
analysisOptionsFile,
testCode.ranges.first.sourceRange.offset,
testCode.ranges.first.sourceRange.length,
),
],
),
]);
}
Future<void> test_incompatible_rule_map() async {
var testCode = TestCode.parse('''
linter:
rules:
/*[0*/rule_pos/*0]*/: true
/*[1*/rule_neg/*1]*/: true
''');
await assertErrorsInCode(testCode.code, [
ExpectedError(
AnalysisOptionsWarningCode.incompatibleLint,
testCode.ranges.last.sourceRange.offset,
testCode.ranges.last.sourceRange.length,
expectedContextMessages: [
ExpectedContextMessage(
analysisOptionsFile,
testCode.ranges.first.sourceRange.offset,
testCode.ranges.first.sourceRange.length,
),
],
),
]);
}
void test_incompatible_rule_map_disabled() {
assertNoErrors('''
linter:
rules:
rule_pos: true
rule_neg: false
''');
}
void test_no_duplicated_rule_include() {
newFile('/included.yaml', '''
linter:
rules:
- stable_lint
''');
assertNoErrors('''
include: included.yaml
linter:
rules:
- stable_lint
''');
}
void test_removed_rule() {
assertErrors(
'''
linter:
rules:
- removed_in_2_12_lint
''',
[AnalysisOptionsWarningCode.removedLint],
sdk: dart2_12,
);
}
void test_removed_rule_notYet_ok() {
assertNoErrors('''
linter:
rules:
- removed_in_2_12_lint
''', sdk: Version(2, 11, 0));
}
void test_replaced_rule() {
assertErrors(
'''
linter:
rules:
- replaced_lint
''',
[AnalysisOptionsWarningCode.replacedLint],
sdk: dart3,
);
}
void test_stable_rule() {
assertNoErrors('''
linter:
rules:
- stable_lint
''');
}
void test_stable_rule_map() {
assertNoErrors('''
linter:
rules:
stable_lint: true
''');
}
void test_undefined_rule() {
assertErrors(
'''
linter:
rules:
- this_rule_does_not_exist
''',
[AnalysisOptionsWarningCode.undefinedLint],
);
}
void test_undefined_rule_map() {
assertErrors(
'''
linter:
rules:
this_rule_does_not_exist: false
''',
[AnalysisOptionsWarningCode.undefinedLint],
);
}
}
mixin OptionsRuleValidatorTestMixin on AbstractAnalysisOptionsTest {
/// Assert that when the validator is used on the given [content] the
/// [expectedCodes] are produced.
void assertErrors(
String content,
List<DiagnosticCode> expectedCodes, {
VersionConstraint? sdk,
}) {
GatheringDiagnosticListener listener = GatheringDiagnosticListener();
var reporter = DiagnosticReporter(
listener,
StringSource(content, 'analysis_options.yaml'),
);
var source = StringSource(content, 'analysis_options.yaml');
var validator = LinterRuleOptionsValidator(
optionsProvider: AnalysisOptionsProvider(sourceFactory),
sdkVersionConstraint: sdk,
resourceProvider: resourceProvider,
);
validator.validate(
reporter,
loadYamlNode(content, sourceUrl: source.uri) as YamlMap,
);
listener.assertErrorsWithCodes(expectedCodes);
}
/// Assert that when the validator is used on the given [content] no errors
/// are produced.
void assertNoErrors(String content, {VersionConstraint? sdk}) =>
assertErrors(content, const [], sdk: sdk);
@override
void setUp() {
registerLintRules([
DeprecatedLint(),
DeprecatedSince3Lint(),
DeprecatedLintWithReplacement(),
StableLint(),
RuleNeg(),
RulePos(),
RemovedIn2_12Lint(),
ReplacedLint(),
ReplacingLint(),
]);
super.setUp();
}
}
@reflectiveTest
class OptionsRuleValidatorValueTest extends AbstractAnalysisOptionsTest
with OptionsRuleValidatorTestMixin {
void test_unsuportedValue_invalidValue() {
assertErrors(
'''
linter:
rules:
rule_pos: invalid_value
''',
[AnalysisOptionsWarningCode.unsupportedValue],
);
}
void test_unsuportedValue_validError() {
assertNoErrors('''
include: included.yaml
linter:
rules:
rule_pos: error
''');
}
void test_unsuportedValue_validFalse() {
assertNoErrors('''
include: included.yaml
linter:
rules:
rule_pos: false
''');
}
void test_unsuportedValue_validIgnore() {
assertNoErrors('''
include: included.yaml
linter:
rules:
rule_pos: ignore
''');
}
void test_unsuportedValue_validInfo() {
assertNoErrors('''
include: included.yaml
linter:
rules:
rule_pos: info
''');
}
void test_unsuportedValue_validTrue() {
assertNoErrors('''
include: included.yaml
linter:
rules:
rule_pos: true
''');
}
void test_unsuportedValue_validWarning() {
assertNoErrors('''
include: included.yaml
linter:
rules:
rule_pos: warning
''');
}
}
class RemovedIn2_12Lint extends TestLintRule {
RemovedIn2_12Lint()
: super(
name: 'removed_in_2_12_lint',
state: RuleState.removed(since: dart2_12),
);
}
class ReplacedLint extends TestLintRule {
ReplacedLint()
: super(
name: 'replaced_lint',
state: RuleState.removed(since: dart3, replacedBy: 'replacing_lint'),
);
}
class ReplacingLint extends TestLintRule {
ReplacingLint() : super(name: 'replacing_lint');
}
class RuleNeg extends TestLintRule {
RuleNeg() : super(name: 'rule_neg');
@override
List<String> get incompatibleRules => ['rule_pos'];
}
class RulePos extends TestLintRule {
RulePos() : super(name: 'rule_pos');
@override
List<String> get incompatibleRules => ['rule_neg'];
}
class StableLint extends TestLintRule {
StableLint() : super(name: 'stable_lint', state: RuleState.stable());
}
abstract class TestLintRule extends LintRule {
static const LintCode code = LintCode(
'lint_code',
'Lint code.',
correctionMessage: 'Lint code.',
);
TestLintRule({required super.name, super.state}) : super(description: '');
@override
DiagnosticCode get diagnosticCode => code;
}