Skip to content
This repository was archived by the owner on Jul 16, 2023. It is now read-only.

feat: support ignoring nesting for prefer-conditional-expressions #1122

Merged
merged 2 commits into from
Dec 28, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## Unreleased

* feat: support ignoring nesting for [`prefer-conditional-expressions`](https://dartcodemetrics.dev/docs/rules/common/prefer-conditional-expressions).
* fix: ignore Providers for ['avoid-returning-widgets'](https://dartcodemetrics.dev/docs/rules/common/avoid-returning-widgets).
* feat: add [`use-setstate-synchronously`](https://dartcodemetrics.dev/docs/rules/flutter/use-setstate-synchronously).

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
part of 'prefer_conditional_expressions_rule.dart';

class _ConfigParser {
static const _ignoreNestedConfig = 'ignore-nested';

static bool parseIgnoreNested(Map<String, Object> config) =>
(config[_ignoreNestedConfig] as bool?) ?? false;
}
Original file line number Diff line number Diff line change
Expand Up @@ -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/)
Expand All @@ -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<String, Object> config = const {}])
: super(
: _ignoreNested = _ConfigParser.parseIgnoreNested(config),
super(
id: ruleId,
severity: readSeverity(config, Severity.style),
excludes: readExcludes(config),
includes: readIncludes(config),
);

@override
Map<String, Object?> toJson() {
final json = super.toJson();
json[_ConfigParser._ignoreNestedConfig] = _ignoreNested;

return json;
}

@override
Iterable<Issue> check(InternalResolvedUnitResult source) {
final visitor = _Visitor();
final visitor = _Visitor(_ignoreNested);

source.unit.visitChildren(visitor);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,26 @@ part of 'prefer_conditional_expressions_rule.dart';
class _Visitor extends RecursiveAstVisitor<void> {
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) {
Expand Down Expand Up @@ -80,6 +94,17 @@ class _Visitor extends RecursiveAstVisitor<void> {
}
}

class _ConditionalsVisitor extends RecursiveAstVisitor<void> {
bool hasInnerConditionals = false;

@override
void visitConditionalExpression(ConditionalExpression node) {
hasInnerConditionals = true;

super.visitConditionalExpression(node);
}
}

class _StatementInfo {
final IfStatement statement;
final AstNode unwrappedThenStatement;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,22 @@ class _ConfigParser {
static const _ignoreNames = 'ignore-names';
static const _ignoreAnnotations = 'ignore-annotations';

static const _defaultIgnorePrivate = false;
static const _defaultIgnoreNames = <String>[];
static const _defaultIgnoreAnnotations = [
...functionalWidgetAnnotations,
'riverpod',
];

static bool getIgnorePrivate(Map<String, Object> config) =>
config[_ignorePrivate] as bool? ?? _defaultIgnorePrivate;
static bool parseIgnorePrivate(Map<String, Object> config) =>
config[_ignorePrivate] as bool? ?? false;

static Iterable<String> getIgnoreNames(Map<String, Object> config) =>
_getIterable(config, _ignoreNames) ?? _defaultIgnoreNames;
static Iterable<String> parseIgnoreNames(Map<String, Object> config) =>
_parseIterable(config, _ignoreNames) ?? _defaultIgnoreNames;

static Iterable<String> getIgnoreAnnotations(Map<String, Object> config) =>
_getIterable(config, _ignoreAnnotations) ?? _defaultIgnoreAnnotations;
static Iterable<String> parseIgnoreAnnotations(Map<String, Object> config) =>
_parseIterable(config, _ignoreAnnotations) ?? _defaultIgnoreAnnotations;

static Iterable<String>? _getIterable(
static Iterable<String>? _parseIterable(
Map<String, Object> config,
String name,
) =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@ class PreferStaticClassRule extends CommonRule {
final Iterable<String> _ignoreAnnotations;

PreferStaticClassRule([Map<String, Object> 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),
Expand Down
Original file line number Diff line number Diff line change
@@ -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 {}
Original file line number Diff line number Diff line change
Expand Up @@ -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', () {
Expand Down Expand Up @@ -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);
});
});
}
15 changes: 14 additions & 1 deletion website/docs/rules/common/prefer-conditional-expressions.mdx
Original file line number Diff line number Diff line change
@@ -1,9 +1,22 @@
import RuleDetails from '@site/src/components/RuleDetails';

<RuleDetails version="1.8.0" severity="style" hasFix />
<RuleDetails version="1.8.0" severity="style" 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.

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:**
Expand Down
1 change: 1 addition & 0 deletions website/docs/rules/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down