| // 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; |
| } |