Skip to content

Commit 68a88ff

Browse files
refactor: migrate avoid_unnecessary_type_casts
1 parent c54bc50 commit 68a88ff

6 files changed

Lines changed: 182 additions & 105 deletions

File tree

lib/main.dart

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import 'package:solid_lints/src/lints/avoid_global_state/avoid_global_state_rule
88
import 'package:solid_lints/src/lints/avoid_non_null_assertion/avoid_non_null_assertion_rule.dart';
99
import 'package:solid_lints/src/lints/avoid_returning_widgets/avoid_returning_widgets_rule.dart';
1010
import 'package:solid_lints/src/lints/avoid_returning_widgets/models/avoid_returning_widgets_parameters.dart';
11+
import 'package:solid_lints/src/lints/avoid_unnecessary_type_casts/avoid_unnecessary_type_casts_rule.dart';
12+
import 'package:solid_lints/src/lints/avoid_unnecessary_type_casts/fixes/avoid_unnecessary_type_casts_fix.dart';
1113
import 'package:solid_lints/src/lints/double_literal_format/double_literal_format_rule.dart';
1214
import 'package:solid_lints/src/lints/double_literal_format/fixes/double_literal_format_fix.dart';
1315
import 'package:solid_lints/src/lints/proper_super_calls/proper_super_calls_rule.dart';
@@ -31,8 +33,11 @@ class SolidLintsPlugin extends Plugin {
3133
final analysisLoader = AnalysisOptionsLoader();
3234

3335
final doubleLiteralFormatRule = DoubleLiteralFormatRule();
36+
final avoidUnnecessaryTypeCastsRule = AvoidUnnecessaryTypeCastsRule();
37+
3438
final lintRules = [
3539
AvoidFinalWithGetterRule(),
40+
avoidUnnecessaryTypeCastsRule,
3641
AvoidGlobalStateRule(),
3742
AvoidNonNullAssertionRule(),
3843
AvoidDebugPrintInReleaseRule(),
@@ -56,5 +61,10 @@ class SolidLintsPlugin extends Plugin {
5661
AvoidFinalWithGetterRule.code,
5762
AvoidFinalWithGetterFix.new,
5863
);
64+
65+
registry.registerFixForRule(
66+
avoidUnnecessaryTypeCastsRule.diagnosticCode,
67+
AvoidUnnecessaryTypeCastsFix.new,
68+
);
5969
}
6070
}
Lines changed: 20 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,37 @@
1-
import 'package:analyzer/dart/ast/ast.dart';
2-
import 'package:analyzer/diagnostic/diagnostic.dart';
3-
import 'package:analyzer/error/listener.dart' as error_listener;
4-
import 'package:analyzer/source/source_range.dart';
5-
import 'package:custom_lint_builder/custom_lint_builder.dart';
1+
import 'package:analyzer/analysis_rule/rule_context.dart';
2+
import 'package:analyzer/analysis_rule/rule_visitor_registry.dart';
3+
import 'package:analyzer/error/error.dart';
64
import 'package:solid_lints/src/lints/avoid_unnecessary_type_casts/visitors/avoid_unnecessary_type_casts_visitor.dart';
7-
import 'package:solid_lints/src/models/rule_config.dart';
85
import 'package:solid_lints/src/models/solid_lint_rule.dart';
96

10-
part 'fixes/avoid_unnecessary_type_casts_fix.dart';
11-
127
/// An `avoid_unnecessary_type_casts` rule which
138
/// warns about unnecessary usage of `as` operator
149
class AvoidUnnecessaryTypeCastsRule extends SolidLintRule {
1510
/// This lint rule represents
1611
/// the error whether we use bad formatted double literals.
1712
static const lintName = 'avoid_unnecessary_type_casts';
1813

19-
AvoidUnnecessaryTypeCastsRule._(super.config);
14+
static const LintCode _code = LintCode(
15+
lintName,
16+
"Avoid unnecessary usage of as operator.",
17+
);
2018

21-
/// Creates a new instance of [AvoidUnnecessaryTypeCastsRule]
22-
/// based on the lint configuration.
23-
factory AvoidUnnecessaryTypeCastsRule.createRule(CustomLintConfigs configs) {
24-
final rule = RuleConfig(
25-
configs: configs,
26-
name: lintName,
27-
problemMessage: (_) => "Avoid unnecessary usage of as operator.",
28-
);
19+
@override
20+
LintCode get diagnosticCode => _code;
2921

30-
return AvoidUnnecessaryTypeCastsRule._(rule);
31-
}
22+
/// Creates a new instance of [AvoidUnnecessaryTypeCastsRule]
23+
AvoidUnnecessaryTypeCastsRule()
24+
: super(
25+
name: lintName,
26+
description: _code.problemMessage,
27+
);
3228

3329
@override
34-
void run(
35-
CustomLintResolver resolver,
36-
error_listener.DiagnosticReporter reporter,
37-
CustomLintContext context,
30+
void registerNodeProcessors(
31+
RuleVisitorRegistry registry,
32+
RuleContext context,
3833
) {
39-
context.registry.addAsExpression((node) {
40-
final visitor = AvoidUnnecessaryTypeCastsVisitor();
41-
visitor.visitAsExpression(node);
42-
43-
for (final element in visitor.expressions.entries) {
44-
reporter.atNode(element.key, code);
45-
}
46-
});
34+
final visitor = AvoidUnnecessaryTypeCastsVisitor(this);
35+
registry.addAsExpression(this, visitor);
4736
}
48-
49-
@override
50-
List<Fix> getFixes() => [_UnnecessaryTypeCastsFix()];
5137
}

lib/src/lints/avoid_unnecessary_type_casts/fixes/avoid_unnecessary_type_casts_fix.dart

Lines changed: 39 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,47 @@
1-
part of '../avoid_unnecessary_type_casts_rule.dart';
1+
import 'package:analysis_server_plugin/edit/dart/correction_producer.dart';
2+
import 'package:analysis_server_plugin/edit/dart/dart_fix_kind_priority.dart';
3+
import 'package:analyzer/dart/ast/ast.dart';
4+
import 'package:analyzer/source/source_range.dart';
5+
import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart';
6+
import 'package:analyzer_plugin/utilities/fixes/fixes.dart';
7+
import 'package:solid_lints/src/lints/avoid_unnecessary_type_casts/avoid_unnecessary_type_casts_rule.dart';
28

39
/// A Quick fix for `avoid_unnecessary_type_casts` rule
410
/// Suggests to remove unnecessary assertions
5-
class _UnnecessaryTypeCastsFix extends DartFix {
11+
class AvoidUnnecessaryTypeCastsFix extends ParsedCorrectionProducer {
12+
static const _avoidUnnecessaryTypeCastsKind = FixKind(
13+
'solid_lints.fix.${AvoidUnnecessaryTypeCastsRule.lintName}',
14+
DartFixKindPriority.standard,
15+
"Remove unnecessary type cast",
16+
);
17+
618
@override
7-
void run(
8-
CustomLintResolver resolver,
9-
ChangeReporter reporter,
10-
CustomLintContext context,
11-
Diagnostic analysisError,
12-
List<Diagnostic> others,
13-
) {
14-
context.registry.addAsExpression((node) {
15-
if (analysisError.sourceRange.intersects(node.sourceRange)) {
16-
_addDeletion(reporter, 'as', node, node.asOperator.offset);
17-
}
18-
});
19-
}
19+
FixKind get fixKind => _avoidUnnecessaryTypeCastsKind;
20+
21+
@override
22+
FixKind get multiFixKind => const FixKind(
23+
'solid_lints.fix.multi.${AvoidUnnecessaryTypeCastsRule.lintName}',
24+
DartFixKindPriority.standard,
25+
"Remove unnecessary type cast across files",
26+
);
27+
28+
/// Creates a new instance of [AvoidUnnecessaryTypeCastsFix]
29+
AvoidUnnecessaryTypeCastsFix({required super.context});
30+
31+
@override
32+
CorrectionApplicability get applicability =>
33+
CorrectionApplicability.automatically;
34+
35+
@override
36+
Future<void> compute(ChangeBuilder builder) async {
37+
final asExpressionNode = node.thisOrAncestorOfType<AsExpression>();
38+
if (asExpressionNode == null) return;
39+
40+
await builder.addDartFileEdit(file, (builder) {
41+
final operatorOffset = asExpressionNode.asOperator.offset;
42+
final targetNameLength = operatorOffset - node.offset;
43+
final removedPartLength = node.length - targetNameLength;
2044

21-
void _addDeletion(
22-
ChangeReporter reporter,
23-
String itemToDelete,
24-
Expression node,
25-
int operatorOffset,
26-
) {
27-
final targetNameLength = operatorOffset - node.offset;
28-
final removedPartLength = node.length - targetNameLength;
29-
30-
final changeBuilder = reporter.createChangeBuilder(
31-
message: "Remove unnecessary '$itemToDelete'",
32-
priority: 1,
33-
);
34-
35-
changeBuilder.addDartFileEdit((builder) {
3645
builder.addDeletion(SourceRange(operatorOffset, removedPartLength));
3746
});
3847
}

lib/src/lints/avoid_unnecessary_type_casts/visitors/avoid_unnecessary_type_casts_visitor.dart

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,16 @@
2323

2424
import 'package:analyzer/dart/ast/ast.dart';
2525
import 'package:analyzer/dart/ast/visitor.dart';
26+
import 'package:solid_lints/src/lints/avoid_unnecessary_type_casts/avoid_unnecessary_type_casts_rule.dart';
2627
import 'package:solid_lints/src/utils/typecast_utils.dart';
2728

2829
/// AST Visitor which finds all as expressions and checks if they are
2930
/// necessary
3031
class AvoidUnnecessaryTypeCastsVisitor extends RecursiveAstVisitor<void> {
31-
final _expressions = <Expression, String>{};
32+
final AvoidUnnecessaryTypeCastsRule _rule;
3233

33-
/// All as expressions
34-
Map<Expression, String> get expressions => _expressions;
34+
/// Creates a new instance of [AvoidUnnecessaryTypeCastsVisitor]
35+
AvoidUnnecessaryTypeCastsVisitor(this._rule);
3536

3637
@override
3738
void visitAsExpression(AsExpression node) {
@@ -49,8 +50,8 @@ class AvoidUnnecessaryTypeCastsVisitor extends RecursiveAstVisitor<void> {
4950
target: castedType,
5051
);
5152

52-
if (typeCast.isUnnecessaryTypeCheck) {
53-
_expressions[node] = 'as';
54-
}
53+
if (!typeCast.isUnnecessaryTypeCheck) return;
54+
55+
_rule.reportAtNode(node);
5556
}
5657
}

lint_test/avoid_unnecessary_type_casts_test.dart

Lines changed: 0 additions & 35 deletions
This file was deleted.
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
import 'package:analyzer/src/diagnostic/diagnostic.dart' as diag;
2+
import 'package:analyzer_testing/analysis_rule/analysis_rule.dart';
3+
import 'package:solid_lints/src/lints/avoid_unnecessary_type_casts/avoid_unnecessary_type_casts_rule.dart';
4+
import 'package:test_reflective_loader/test_reflective_loader.dart';
5+
6+
void main() {
7+
defineReflectiveSuite(() {
8+
defineReflectiveTests(AvoidUnnecessaryTypeCastsRuleTest);
9+
});
10+
}
11+
12+
@reflectiveTest
13+
class AvoidUnnecessaryTypeCastsRuleTest extends AnalysisRuleTest {
14+
@override
15+
void setUp() {
16+
rule = AvoidUnnecessaryTypeCastsRule();
17+
super.setUp();
18+
}
19+
20+
@override
21+
String get analysisRule => rule.name;
22+
23+
void test_reports_on_list_cast() async {
24+
await assertDiagnostics(
25+
r'''
26+
void fun() {
27+
final testList = [1.0, 2.0, 3.0];
28+
29+
final result = testList as List<double>;
30+
}
31+
''',
32+
[
33+
error(diag.unnecessaryCast, 67, 24),
34+
lint(67, 24),
35+
],
36+
);
37+
}
38+
39+
void test_reports_on_map_value_cast_to_nullable() async {
40+
await assertDiagnostics(
41+
r'''
42+
void fun() {
43+
final testMap = {'A': 'B'};
44+
45+
final castedMapValue = testMap['A'] as String?;
46+
}
47+
''',
48+
[
49+
error(diag.unnecessaryCast, 69, 23),
50+
lint(69, 23),
51+
],
52+
);
53+
}
54+
55+
void test_reports_on_argument_cast() async {
56+
await assertDiagnostics(
57+
r'''
58+
void fun() {
59+
final testString = 'String';
60+
61+
_testFun(testString as String);
62+
}
63+
64+
void _testFun(String a) {}
65+
''',
66+
[
67+
error(diag.unnecessaryCast, 56, 20),
68+
lint(56, 20),
69+
],
70+
);
71+
}
72+
73+
void test_reports_on_parenthesized_cast() async {
74+
await assertDiagnostics(
75+
r'''
76+
void fun(String a) {
77+
final result = (a as String).length;
78+
}
79+
''',
80+
[
81+
error(diag.unnecessaryCast, 39, 11),
82+
lint(39, 11),
83+
],
84+
);
85+
}
86+
87+
void test_does_not_report_on_nullable_to_non_nullable_cast() async {
88+
await assertNoDiagnostics(r'''
89+
void fun() {
90+
final double? nullableD = 2.0;
91+
92+
final castedD = nullableD as double;
93+
}
94+
''');
95+
}
96+
97+
void test_does_not_report_on_map_value_cast_to_non_nullable() async {
98+
await assertNoDiagnostics(r'''
99+
void fun() {
100+
final testMap = {'A': 'B'};
101+
102+
final castedNotNullMapValue = testMap['A'] as String;
103+
}
104+
''');
105+
}
106+
}

0 commit comments

Comments
 (0)