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

Commit 5483156

Browse files
feat: add avoid-unrelated-type-assertions rule (#604)
* feat: add avoid-unrelated-type-assertions rule * chore: fix rule name in the docs * refactor: remove offsets checking from tests (#605) * fix: update edge cases checks * chore: cleanup * fix: fix generics edge case * fix: add support for new cases * fix: add support for new cases * test: add test case * test: add test case * test: add additional test cases Co-authored-by: Dmitry Krutskikh <dmitry.krutskikh@gmail.com>
1 parent 104eb17 commit 5483156

File tree

116 files changed

+433
-728
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

116 files changed

+433
-728
lines changed

CHANGELOG.md

Lines changed: 1 addition & 1 deletion

lib/src/analyzers/lint_analyzer/rules/rules_factory.dart

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import 'models/rule.dart';
22
import 'rules_list/always_remove_listener/always_remove_listener_rule.dart';
3-
import 'rules_list/avoid-unnecessary-type-casts/avoid_unnecessary_type_casts_rule.dart';
43
import 'rules_list/avoid_global_state/avoid_global_state_rule.dart';
54
import 'rules_list/avoid_ignoring_return_values/avoid_ignoring_return_values_rule.dart';
65
import 'rules_list/avoid_late_keyword/avoid_late_keyword_rule.dart';
@@ -12,6 +11,8 @@ import 'rules_list/avoid_returning_widgets/avoid_returning_widgets_rule.dart';
1211
import 'rules_list/avoid_throw_in_catch_block/avoid_throw_in_catch_block_rule.dart';
1312
import 'rules_list/avoid_unnecessary_setstate/avoid_unnecessary_setstate_rule.dart';
1413
import 'rules_list/avoid_unnecessary_type_assertions/avoid_unnecessary_type_assertions_rule.dart';
14+
import 'rules_list/avoid_unnecessary_type_casts/avoid_unnecessary_type_casts_rule.dart';
15+
import 'rules_list/avoid_unrelated_type_assertions/avoid_unrelated_type_assertions_rule.dart';
1516
import 'rules_list/avoid_unused_parameters/avoid_unused_parameters_rule.dart';
1617
import 'rules_list/avoid_wrapping_in_padding/avoid_wrapping_in_padding_rule.dart';
1718
import 'rules_list/binary_expression_operand_order/binary_expression_operand_order_rule.dart';
@@ -42,8 +43,6 @@ import 'rules_list/provide_correct_intl_args/provide_correct_intl_args_rule.dart
4243

4344
final _implementedRules = <String, Rule Function(Map<String, Object>)>{
4445
AlwaysRemoveListenerRule.ruleId: (config) => AlwaysRemoveListenerRule(config),
45-
AvoidUnnecessaryTypeCastsRule.ruleId: (config) =>
46-
AvoidUnnecessaryTypeCastsRule(config),
4746
AvoidGlobalStateRule.ruleId: (config) => AvoidGlobalStateRule(config),
4847
AvoidIgnoringReturnValuesRule.ruleId: (config) =>
4948
AvoidIgnoringReturnValuesRule(config),
@@ -64,6 +63,10 @@ final _implementedRules = <String, Rule Function(Map<String, Object>)>{
6463
AvoidUnnecessarySetStateRule(config),
6564
AvoidUnnecessaryTypeAssertionsRule.ruleId: (config) =>
6665
AvoidUnnecessaryTypeAssertionsRule(config),
66+
AvoidUnnecessaryTypeCastsRule.ruleId: (config) =>
67+
AvoidUnnecessaryTypeCastsRule(config),
68+
AvoidUnrelatedTypeAssertionsRule.ruleId: (config) =>
69+
AvoidUnnecessaryTypeAssertionsRule(config),
6770
AvoidUnusedParametersRule.ruleId: (config) =>
6871
AvoidUnusedParametersRule(config),
6972
AvoidWrappingInPaddingRule.ruleId: (config) =>

lib/src/analyzers/lint_analyzer/rules/rules_list/avoid_unnecessary_type_assertions/avoid_unnecessary_type_assertions_rule.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ class AvoidUnnecessaryTypeAssertionsRule extends CommonRule {
2121
AvoidUnnecessaryTypeAssertionsRule([Map<String, Object> config = const {}])
2222
: super(
2323
id: ruleId,
24-
severity: readSeverity(config, Severity.style),
24+
severity: readSeverity(config, Severity.warning),
2525
excludes: readExcludes(config),
2626
);
2727

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ class AvoidUnnecessaryTypeCastsRule extends CommonRule {
2020
AvoidUnnecessaryTypeCastsRule([Map<String, Object> config = const {}])
2121
: super(
2222
id: ruleId,
23-
severity: readSeverity(config, Severity.style),
23+
severity: readSeverity(config, Severity.warning),
2424
excludes: readExcludes(config),
2525
);
2626

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import 'package:analyzer/dart/ast/ast.dart';
2+
import 'package:analyzer/dart/ast/visitor.dart';
3+
import 'package:analyzer/dart/element/element.dart';
4+
import 'package:analyzer/dart/element/type.dart';
5+
import 'package:collection/collection.dart';
6+
7+
import '../../../../../utils/node_utils.dart';
8+
import '../../../lint_utils.dart';
9+
import '../../../models/internal_resolved_unit_result.dart';
10+
import '../../../models/issue.dart';
11+
import '../../../models/severity.dart';
12+
import '../../models/common_rule.dart';
13+
import '../../rule_utils.dart';
14+
15+
part 'visitor.dart';
16+
17+
class AvoidUnrelatedTypeAssertionsRule extends CommonRule {
18+
static const String ruleId = 'avoid-unrelated-type-assertions';
19+
20+
AvoidUnrelatedTypeAssertionsRule([Map<String, Object> config = const {}])
21+
: super(
22+
id: ruleId,
23+
severity: readSeverity(config, Severity.warning),
24+
excludes: readExcludes(config),
25+
);
26+
27+
@override
28+
Iterable<Issue> check(InternalResolvedUnitResult source) {
29+
final visitor = _Visitor();
30+
31+
source.unit.visitChildren(visitor);
32+
33+
return visitor.expressions.entries
34+
.map(
35+
(node) => createIssue(
36+
rule: this,
37+
location: nodeLocation(node: node.key, source: source),
38+
message:
39+
'Avoid unrelated "is" assertion. The result is always "false".',
40+
),
41+
)
42+
.toList(growable: false);
43+
}
44+
}
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
part of 'avoid_unrelated_type_assertions_rule.dart';
2+
3+
class _Visitor extends RecursiveAstVisitor<void> {
4+
final _expressions = <IsExpression, String>{};
5+
6+
Map<IsExpression, String> get expressions => _expressions;
7+
8+
@override
9+
void visitIsExpression(IsExpression node) {
10+
super.visitIsExpression(node);
11+
12+
if (node.notOperator != null || node.type.type is TypeParameterType) {
13+
return;
14+
}
15+
16+
final objectType = node.expression.staticType;
17+
final castedType = node.type.type;
18+
19+
if (_isUnrelatedTypeCheck(objectType, castedType)) {
20+
_expressions[node] =
21+
'${node.isOperator.keyword?.lexeme ?? ''}${node.notOperator ?? ''}';
22+
}
23+
}
24+
25+
bool _isUnrelatedTypeCheck(DartType? objectType, DartType? castedType) {
26+
if (objectType == null || castedType == null) {
27+
return false;
28+
}
29+
30+
if (objectType.isDynamic || castedType.isDynamic) {
31+
return false;
32+
}
33+
34+
if (objectType is! ParameterizedType || castedType is! ParameterizedType) {
35+
return false;
36+
}
37+
38+
final objectCastedType =
39+
_foundCastedTypeInObjectTypeHierarchy(objectType, castedType);
40+
final castedObjectType =
41+
_foundCastedTypeInObjectTypeHierarchy(castedType, objectType);
42+
if (objectCastedType == null && castedObjectType == null) {
43+
return true;
44+
}
45+
46+
if (objectCastedType != null &&
47+
_checkGenerics(objectCastedType, castedType) &&
48+
castedObjectType != null &&
49+
_checkGenerics(castedObjectType, objectType)) {
50+
return true;
51+
}
52+
53+
return false;
54+
}
55+
56+
DartType? _foundCastedTypeInObjectTypeHierarchy(
57+
DartType objectType,
58+
DartType castedType,
59+
) {
60+
if (_isFutureOrAndFuture(objectType, castedType)) {
61+
return objectType;
62+
}
63+
64+
final correctObjectType =
65+
objectType is InterfaceType && objectType.isDartAsyncFutureOr
66+
? objectType.typeArguments.first
67+
: objectType;
68+
69+
if ((correctObjectType.element == castedType.element) ||
70+
castedType.isDynamic ||
71+
correctObjectType.isDynamic ||
72+
_isObjectAndEnum(correctObjectType, castedType)) {
73+
return correctObjectType;
74+
}
75+
76+
if (correctObjectType is InterfaceType) {
77+
return correctObjectType.allSupertypes
78+
.firstWhereOrNull((value) => value.element == castedType.element);
79+
}
80+
81+
return null;
82+
}
83+
84+
bool _checkGenerics(DartType objectType, DartType castedType) {
85+
if (objectType.isDynamic || castedType.isDynamic) {
86+
return false;
87+
}
88+
89+
if (objectType is! ParameterizedType || castedType is! ParameterizedType) {
90+
return false;
91+
}
92+
93+
if (objectType.typeArguments.length != castedType.typeArguments.length) {
94+
return false;
95+
}
96+
97+
for (var argumentIndex = 0;
98+
argumentIndex < objectType.typeArguments.length;
99+
argumentIndex++) {
100+
final objectGenericType = objectType.typeArguments[argumentIndex];
101+
final castedGenericType = castedType.typeArguments[argumentIndex];
102+
103+
if (_isUnrelatedTypeCheck(objectGenericType, castedGenericType) &&
104+
_isUnrelatedTypeCheck(castedGenericType, objectGenericType)) {
105+
return true;
106+
}
107+
}
108+
109+
return false;
110+
}
111+
112+
bool _isFutureOrAndFuture(DartType objectType, DartType castedType) =>
113+
objectType.isDartAsyncFutureOr && castedType.isDartAsyncFuture;
114+
115+
bool _isObjectAndEnum(DartType objectType, DartType castedType) =>
116+
objectType.isDartCoreObject &&
117+
castedType.element?.kind == ElementKind.ENUM;
118+
}

test/src/analyzer_plugin/analyzer_plugin_utils_test.dart

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
@TestOn('vm')
21
import 'package:analyzer/dart/analysis/results.dart';
32
import 'package:analyzer_plugin/protocol/protocol_common.dart';
43
import 'package:dart_code_metrics/src/analyzer_plugin/analyzer_plugin_utils.dart';

test/src/analyzers/lint_analyzer/anti_patterns/anti_patterns_list/long_method/long_method_test.dart

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
@TestOn('vm')
21
import 'package:analyzer/dart/ast/ast.dart';
32
import 'package:dart_code_metrics/src/analyzers/lint_analyzer/anti_patterns/anti_patterns_list/long_method.dart';
43
import 'package:dart_code_metrics/src/analyzers/lint_analyzer/metrics/metrics_list/source_lines_of_code/source_lines_of_code_metric.dart';

test/src/analyzers/lint_analyzer/anti_patterns/anti_patterns_list/long_parameter_list/long_parameter_list_test.dart

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
@TestOn('vm')
21
import 'package:analyzer/dart/ast/ast.dart';
32
import 'package:dart_code_metrics/src/analyzers/lint_analyzer/anti_patterns/anti_patterns_list/long_parameter_list.dart';
43
import 'package:dart_code_metrics/src/analyzers/lint_analyzer/metrics/metrics_list/number_of_parameters_metric.dart';

0 commit comments

Comments
 (0)