From 5b1724e37d90b598c49b40878e4c889bdbb62aab Mon Sep 17 00:00:00 2001 From: Dmitry Zhifarsky Date: Mon, 26 Dec 2022 15:03:58 +0400 Subject: [PATCH] feat: support ignoring nesting for prefer-conditional-expressions --- CHANGELOG.md | 4 +++ .../config_parser.dart | 8 ++++++ .../prefer_conditional_expressions_rule.dart | 16 ++++++++++-- .../visitor.dart | 25 +++++++++++++++++++ .../prefer_static_class/config_parser.dart | 15 ++++++----- .../prefer_static_class_rule.dart | 6 ++--- .../examples/nested_example.dart | 21 ++++++++++++++++ ...fer_conditional_expressions_rule_test.dart | 10 ++++++++ .../common/prefer-conditional-expressions.mdx | 15 ++++++++++- website/docs/rules/index.mdx | 1 + 10 files changed, 107 insertions(+), 14 deletions(-) create mode 100644 lib/src/analyzers/lint_analyzer/rules/rules_list/prefer_conditional_expressions/config_parser.dart create mode 100644 test/src/analyzers/lint_analyzer/rules/rules_list/prefer_conditional_expressions/examples/nested_example.dart diff --git a/CHANGELOG.md b/CHANGELOG.md index c495cdc75f..f23b27f8b3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## Unreleased + +* feat: support ignoring nesting for [`prefer-conditional-expressions`](https://dartcodemetrics.dev/docs/rules/common/prefer-conditional-expressions). + ## 5.3.0 * feat: add static code diagnostic [`list-all-equatable-fields`](https://dartcodemetrics.dev/docs/rules/common/list-all-equatable-fields). diff --git a/lib/src/analyzers/lint_analyzer/rules/rules_list/prefer_conditional_expressions/config_parser.dart b/lib/src/analyzers/lint_analyzer/rules/rules_list/prefer_conditional_expressions/config_parser.dart new file mode 100644 index 0000000000..e15e3e0cba --- /dev/null +++ b/lib/src/analyzers/lint_analyzer/rules/rules_list/prefer_conditional_expressions/config_parser.dart @@ -0,0 +1,8 @@ +part of 'prefer_conditional_expressions_rule.dart'; + +class _ConfigParser { + static const _ignoreNestedConfig = 'ignore-nested'; + + static bool parseIgnoreNested(Map config) => + (config[_ignoreNestedConfig] as bool?) ?? false; +} diff --git a/lib/src/analyzers/lint_analyzer/rules/rules_list/prefer_conditional_expressions/prefer_conditional_expressions_rule.dart b/lib/src/analyzers/lint_analyzer/rules/rules_list/prefer_conditional_expressions/prefer_conditional_expressions_rule.dart index e43a4da8ce..cd909a5d94 100644 --- a/lib/src/analyzers/lint_analyzer/rules/rules_list/prefer_conditional_expressions/prefer_conditional_expressions_rule.dart +++ b/lib/src/analyzers/lint_analyzer/rules/rules_list/prefer_conditional_expressions/prefer_conditional_expressions_rule.dart @@ -13,6 +13,7 @@ import '../../../models/severity.dart'; import '../../models/common_rule.dart'; import '../../rule_utils.dart'; +part 'config_parser.dart'; part 'visitor.dart'; // Inspired by TSLint (https://palantir.github.io/tslint/rules/prefer-conditional-expression/) @@ -23,17 +24,28 @@ class PreferConditionalExpressionsRule extends CommonRule { static const _warningMessage = 'Prefer conditional expression.'; static const _correctionMessage = 'Convert to conditional expression.'; + final bool _ignoreNested; + PreferConditionalExpressionsRule([Map config = const {}]) - : super( + : _ignoreNested = _ConfigParser.parseIgnoreNested(config), + super( id: ruleId, severity: readSeverity(config, Severity.style), excludes: readExcludes(config), includes: readIncludes(config), ); + @override + Map toJson() { + final json = super.toJson(); + json[_ConfigParser._ignoreNestedConfig] = _ignoreNested; + + return json; + } + @override Iterable check(InternalResolvedUnitResult source) { - final visitor = _Visitor(); + final visitor = _Visitor(_ignoreNested); source.unit.visitChildren(visitor); diff --git a/lib/src/analyzers/lint_analyzer/rules/rules_list/prefer_conditional_expressions/visitor.dart b/lib/src/analyzers/lint_analyzer/rules/rules_list/prefer_conditional_expressions/visitor.dart index 0f13a36fb9..717d282012 100644 --- a/lib/src/analyzers/lint_analyzer/rules/rules_list/prefer_conditional_expressions/visitor.dart +++ b/lib/src/analyzers/lint_analyzer/rules/rules_list/prefer_conditional_expressions/visitor.dart @@ -3,12 +3,26 @@ part of 'prefer_conditional_expressions_rule.dart'; class _Visitor extends RecursiveAstVisitor { final _statementsInfo = <_StatementInfo>[]; + final bool _ignoreNested; + Iterable<_StatementInfo> get statementsInfo => _statementsInfo; + // ignore: avoid_positional_boolean_parameters + _Visitor(this._ignoreNested); + @override void visitIfStatement(IfStatement node) { super.visitIfStatement(node); + if (_ignoreNested) { + final visitor = _ConditionalsVisitor(); + node.visitChildren(visitor); + + if (visitor.hasInnerConditionals) { + return; + } + } + if (node.parent is! IfStatement && node.elseStatement != null && node.elseStatement is! IfStatement) { @@ -80,6 +94,17 @@ class _Visitor extends RecursiveAstVisitor { } } +class _ConditionalsVisitor extends RecursiveAstVisitor { + bool hasInnerConditionals = false; + + @override + void visitConditionalExpression(ConditionalExpression node) { + hasInnerConditionals = true; + + super.visitConditionalExpression(node); + } +} + class _StatementInfo { final IfStatement statement; final AstNode unwrappedThenStatement; diff --git a/lib/src/analyzers/lint_analyzer/rules/rules_list/prefer_static_class/config_parser.dart b/lib/src/analyzers/lint_analyzer/rules/rules_list/prefer_static_class/config_parser.dart index 5c84223238..ef66210859 100644 --- a/lib/src/analyzers/lint_analyzer/rules/rules_list/prefer_static_class/config_parser.dart +++ b/lib/src/analyzers/lint_analyzer/rules/rules_list/prefer_static_class/config_parser.dart @@ -5,23 +5,22 @@ class _ConfigParser { static const _ignoreNames = 'ignore-names'; static const _ignoreAnnotations = 'ignore-annotations'; - static const _defaultIgnorePrivate = false; static const _defaultIgnoreNames = []; static const _defaultIgnoreAnnotations = [ ...functionalWidgetAnnotations, 'riverpod', ]; - static bool getIgnorePrivate(Map config) => - config[_ignorePrivate] as bool? ?? _defaultIgnorePrivate; + static bool parseIgnorePrivate(Map config) => + config[_ignorePrivate] as bool? ?? false; - static Iterable getIgnoreNames(Map config) => - _getIterable(config, _ignoreNames) ?? _defaultIgnoreNames; + static Iterable parseIgnoreNames(Map config) => + _parseIterable(config, _ignoreNames) ?? _defaultIgnoreNames; - static Iterable getIgnoreAnnotations(Map config) => - _getIterable(config, _ignoreAnnotations) ?? _defaultIgnoreAnnotations; + static Iterable parseIgnoreAnnotations(Map config) => + _parseIterable(config, _ignoreAnnotations) ?? _defaultIgnoreAnnotations; - static Iterable? _getIterable( + static Iterable? _parseIterable( Map config, String name, ) => diff --git a/lib/src/analyzers/lint_analyzer/rules/rules_list/prefer_static_class/prefer_static_class_rule.dart b/lib/src/analyzers/lint_analyzer/rules/rules_list/prefer_static_class/prefer_static_class_rule.dart index e0ee30ed9a..044424b59e 100644 --- a/lib/src/analyzers/lint_analyzer/rules/rules_list/prefer_static_class/prefer_static_class_rule.dart +++ b/lib/src/analyzers/lint_analyzer/rules/rules_list/prefer_static_class/prefer_static_class_rule.dart @@ -27,9 +27,9 @@ class PreferStaticClassRule extends CommonRule { final Iterable _ignoreAnnotations; PreferStaticClassRule([Map config = const {}]) - : _ignorePrivate = _ConfigParser.getIgnorePrivate(config), - _ignoreAnnotations = _ConfigParser.getIgnoreAnnotations(config), - _ignoreNames = _ConfigParser.getIgnoreNames(config), + : _ignorePrivate = _ConfigParser.parseIgnorePrivate(config), + _ignoreAnnotations = _ConfigParser.parseIgnoreAnnotations(config), + _ignoreNames = _ConfigParser.parseIgnoreNames(config), super( id: ruleId, severity: readSeverity(config, Severity.style), diff --git a/test/src/analyzers/lint_analyzer/rules/rules_list/prefer_conditional_expressions/examples/nested_example.dart b/test/src/analyzers/lint_analyzer/rules/rules_list/prefer_conditional_expressions/examples/nested_example.dart new file mode 100644 index 0000000000..6f78d1bb85 --- /dev/null +++ b/test/src/analyzers/lint_analyzer/rules/rules_list/prefer_conditional_expressions/examples/nested_example.dart @@ -0,0 +1,21 @@ +void main() { + String? value = 'value'; + + final condition = true; + if (condition) { + value = !condition ? 'new' : 'old'; + } else { + value = null; + } + + Widget traceColor; + Widget? image; + + if (condition != null) { + traceColor = condition ? Widget() : Widget(); + } else { + traceColor = image == null ? Widget() : Widget(); + } +} + +class Widget {} diff --git a/test/src/analyzers/lint_analyzer/rules/rules_list/prefer_conditional_expressions/prefer_conditional_expressions_rule_test.dart b/test/src/analyzers/lint_analyzer/rules/rules_list/prefer_conditional_expressions/prefer_conditional_expressions_rule_test.dart index a7e196d1b8..227b3356c7 100644 --- a/test/src/analyzers/lint_analyzer/rules/rules_list/prefer_conditional_expressions/prefer_conditional_expressions_rule_test.dart +++ b/test/src/analyzers/lint_analyzer/rules/rules_list/prefer_conditional_expressions/prefer_conditional_expressions_rule_test.dart @@ -5,6 +5,8 @@ import 'package:test/test.dart'; import '../../../../../helpers/rule_test_helper.dart'; const _examplePath = 'prefer_conditional_expressions/examples/example.dart'; +const _nestedExamplePath = + 'prefer_conditional_expressions/examples/nested_example.dart'; void main() { group('PreferConditionalExpressionsRule', () { @@ -137,5 +139,13 @@ void main() { ], ); }); + + test('reports no issues for nested conditionals', () async { + final unit = await RuleTestHelper.resolveFromFile(_nestedExamplePath); + final issues = + PreferConditionalExpressionsRule({'ignore-nested': true}).check(unit); + + RuleTestHelper.verifyNoIssues(issues); + }); }); } diff --git a/website/docs/rules/common/prefer-conditional-expressions.mdx b/website/docs/rules/common/prefer-conditional-expressions.mdx index 7ef7fb99e3..1e42d5d214 100644 --- a/website/docs/rules/common/prefer-conditional-expressions.mdx +++ b/website/docs/rules/common/prefer-conditional-expressions.mdx @@ -1,9 +1,22 @@ import RuleDetails from '@site/src/components/RuleDetails'; - + Recommends to use a conditional expression instead of assigning to the same thing or return statement in each branch of an if statement. +Use `ignore-nested` configuration, if you want to ignore cases with multiple nested conditional expressions (default is false). + +### ⚙️ Config example {#config-example} + +```yaml +dart_code_metrics: + ... + rules: + ... + - prefer-conditional-expressions: + ignore-nested: true +``` + ### Example {#example} **❌ Bad:** diff --git a/website/docs/rules/index.mdx b/website/docs/rules/index.mdx index 1c465a818f..939a8652a9 100644 --- a/website/docs/rules/index.mdx +++ b/website/docs/rules/index.mdx @@ -400,6 +400,7 @@ Rules are grouped by category to help you understand their purpose. Each rule ha severity="style" version="1.8.0" hasFix + hasConfig > Recommends to use a conditional expression instead of assigning to the same thing or return statement in each branch of an if statement.