This repository was archived by the owner on Jul 16, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 275
feat: add static code diagnostics avoid-unnecessary-type-casts
#512
Merged
dkrutskikh
merged 16 commits into
dart-code-checker:master
from
vlkonoshenko:avoid_useless_type_checks
Nov 18, 2021
Merged
Changes from 15 commits
Commits
Show all changes
16 commits
Select commit
Hold shift + click to select a range
44c52fd
feat: add static code diagnostics `avoid-unnecessary-type-casts`
14d97a6
feat: formatted code
64da6b8
Merge remote-tracking branch 'origin/master' into avoid_useless_type_…
3e555e9
feat: formatted code
e0a2e98
feat: remove as operator
6d392dc
doc: updated documentation
258d66d
doc: updated documentation
b709f5a
fix: fixed compare in rule
be01f71
Merge remote-tracking branch 'origin/master' into avoid_useless_type_…
133ff45
fix: fixed compare in rule
609f5d4
fix: fixed compare in rule
734ce5f
chore: added changelog
9262feb
doc: changed text
2cae45e
Merge remote-tracking branch 'origin/master' into avoid_useless_type_…
9439fd2
chore: refactoring code like with type assertion
998a4d4
review changes
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
43 changes: 43 additions & 0 deletions
43
..._analyzer/rules/rules_list/avoid-unnecessary-type-casts/avoid_unnecessary_type_casts.dart
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
import 'package:analyzer/dart/ast/ast.dart'; | ||
import 'package:analyzer/dart/ast/visitor.dart'; | ||
import 'package:analyzer/dart/element/nullability_suffix.dart'; | ||
import 'package:analyzer/dart/element/type.dart'; | ||
import 'package:collection/collection.dart'; | ||
|
||
import '../../../../../utils/node_utils.dart'; | ||
import '../../../lint_utils.dart'; | ||
import '../../../models/internal_resolved_unit_result.dart'; | ||
import '../../../models/issue.dart'; | ||
import '../../../models/severity.dart'; | ||
import '../../models/common_rule.dart'; | ||
import '../../rule_utils.dart'; | ||
|
||
part 'visitor.dart'; | ||
|
||
class AvoidUnnecessaryTypeCasts extends CommonRule { | ||
static const String ruleId = 'avoid-unnecessary-type-casts'; | ||
|
||
AvoidUnnecessaryTypeCasts([Map<String, Object> config = const {}]) | ||
: super( | ||
id: ruleId, | ||
severity: readSeverity(config, Severity.style), | ||
excludes: readExcludes(config), | ||
); | ||
|
||
@override | ||
Iterable<Issue> check(InternalResolvedUnitResult source) { | ||
final visitor = _Visitor(); | ||
|
||
source.unit.visitChildren(visitor); | ||
|
||
return visitor.expressions.entries | ||
.map( | ||
(node) => createIssue( | ||
rule: this, | ||
location: nodeLocation(node: node.key, source: source), | ||
message: 'Avoid unnecessary "${node.value}" type cast.', | ||
), | ||
) | ||
.toList(growable: false); | ||
} | ||
} |
87 changes: 87 additions & 0 deletions
87
lib/src/analyzers/lint_analyzer/rules/rules_list/avoid-unnecessary-type-casts/visitor.dart
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
part of 'avoid_unnecessary_type_casts.dart'; | ||
|
||
class _Visitor extends RecursiveAstVisitor<void> { | ||
final _expressions = <Expression, String>{}; | ||
|
||
Map<Expression, String> get expressions => _expressions; | ||
|
||
@override | ||
void visitAsExpression(AsExpression node) { | ||
final objectType = node.expression.staticType; | ||
final castedType = node.type.type; | ||
if (_isUselessTypeCheck(objectType, castedType)) { | ||
_expressions[node] = 'as'; | ||
} | ||
} | ||
|
||
bool _isUselessTypeCheck(DartType? objectType, DartType? castedType) { | ||
if (objectType == null || castedType == null) { | ||
return false; | ||
} | ||
|
||
if (_checkNullableCompatibility(objectType, castedType)) { | ||
return false; | ||
} | ||
|
||
final objectCastedType = | ||
_foundCastedTypeInObjectTypeHierarchy(objectType, castedType); | ||
if (objectCastedType == null) { | ||
return false; | ||
} | ||
|
||
if (!_checkGenerics(objectCastedType, castedType)) { | ||
return false; | ||
} | ||
|
||
return true; | ||
} | ||
|
||
bool _checkNullableCompatibility(DartType objectType, DartType castedType) { | ||
final isObjectTypeNullable = | ||
objectType.nullabilitySuffix != NullabilitySuffix.none; | ||
final isCastedTypeNullable = | ||
castedType.nullabilitySuffix != NullabilitySuffix.none; | ||
|
||
// Only one case `Type? is Type` always valid assertion case | ||
return isObjectTypeNullable && !isCastedTypeNullable; | ||
} | ||
|
||
DartType? _foundCastedTypeInObjectTypeHierarchy( | ||
DartType objectType, | ||
DartType castedType, | ||
) { | ||
if (objectType.element == castedType.element) { | ||
return objectType; | ||
} | ||
|
||
if (objectType is InterfaceType) { | ||
return objectType.allSupertypes | ||
.firstWhereOrNull((value) => value.element == castedType.element); | ||
} | ||
|
||
return null; | ||
} | ||
|
||
bool _checkGenerics(DartType objectType, DartType castedType) { | ||
if (objectType is! ParameterizedType || castedType is! ParameterizedType) { | ||
return false; | ||
} | ||
|
||
if (objectType.typeArguments.length != castedType.typeArguments.length) { | ||
return false; | ||
} | ||
|
||
for (var argumentIndex = 0; | ||
argumentIndex < objectType.typeArguments.length; | ||
argumentIndex++) { | ||
if (!_isUselessTypeCheck( | ||
objectType.typeArguments[argumentIndex], | ||
castedType.typeArguments[argumentIndex], | ||
)) { | ||
return false; | ||
} | ||
} | ||
|
||
return true; | ||
} | ||
} |
103 changes: 103 additions & 0 deletions
103
...yzer/rules/rules_list/avoid_unnecessary_type_casts/avoid_unnecessary_type_casts_test.dart
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
import 'package:dart_code_metrics/src/analyzers/lint_analyzer/models/severity.dart'; | ||
import 'package:dart_code_metrics/src/analyzers/lint_analyzer/rules/rules_list/avoid-unnecessary-type-casts/avoid_unnecessary_type_casts.dart'; | ||
import 'package:test/test.dart'; | ||
|
||
import '../../../../../helpers/rule_test_helper.dart'; | ||
|
||
const _path = 'avoid_unnecessary_type_casts/examples/example.dart'; | ||
|
||
void main() { | ||
group('AvoidUnnecessaryTypeCasts', () { | ||
vlkonoshenko marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
test('initialization', () async { | ||
final unit = await RuleTestHelper.resolveFromFile(_path); | ||
final issues = AvoidUnnecessaryTypeCasts().check(unit); | ||
|
||
RuleTestHelper.verifyInitialization( | ||
issues: issues, | ||
ruleId: 'avoid-unnecessary-type-casts', | ||
severity: Severity.style, | ||
); | ||
}); | ||
|
||
test('reports about found all issues in example.dart', () async { | ||
final unit = await RuleTestHelper.resolveFromFile(_path); | ||
final issues = AvoidUnnecessaryTypeCasts().check(unit); | ||
|
||
RuleTestHelper.verifyIssues( | ||
issues: issues, | ||
startOffsets: [ | ||
120, | ||
173, | ||
228, | ||
539, | ||
584, | ||
630, | ||
672, | ||
718, | ||
968, | ||
1089, | ||
], | ||
startLines: [ | ||
6, | ||
7, | ||
8, | ||
21, | ||
22, | ||
23, | ||
24, | ||
25, | ||
32, | ||
40, | ||
], | ||
startColumns: [ | ||
20, | ||
21, | ||
21, | ||
20, | ||
20, | ||
20, | ||
20, | ||
20, | ||
16, | ||
20, | ||
], | ||
endOffsets: [ | ||
143, | ||
198, | ||
252, | ||
555, | ||
601, | ||
643, | ||
689, | ||
731, | ||
986, | ||
1108, | ||
], | ||
locationTexts: [ | ||
'regularString as String', | ||
'nullableString as String?', | ||
'regularString as String?', | ||
'animal as Animal', | ||
'cat as HomeAnimal', | ||
'cat as Animal', | ||
'dog as HomeAnimal', | ||
'dog as Animal', | ||
'regular as String?', | ||
'myList as List<int>', | ||
], | ||
messages: [ | ||
'Avoid unnecessary "as" type cast.', | ||
'Avoid unnecessary "as" type cast.', | ||
'Avoid unnecessary "as" type cast.', | ||
'Avoid unnecessary "as" type cast.', | ||
'Avoid unnecessary "as" type cast.', | ||
'Avoid unnecessary "as" type cast.', | ||
'Avoid unnecessary "as" type cast.', | ||
'Avoid unnecessary "as" type cast.', | ||
'Avoid unnecessary "as" type cast.', | ||
'Avoid unnecessary "as" type cast.', | ||
], | ||
); | ||
}); | ||
}); | ||
} |
50 changes: 50 additions & 0 deletions
50
...alyzers/lint_analyzer/rules/rules_list/avoid_unnecessary_type_casts/examples/example.dart
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
class Example1 { | ||
final regularString = ''; | ||
final String? nullableString = null; | ||
|
||
void main() { | ||
final result = regularString as String; // LINT | ||
final result2 = nullableString as String?; // LINT | ||
final result3 = regularString as String?; // LINT | ||
final result4 = nullableString as String; | ||
} | ||
} | ||
|
||
class Example2 { | ||
final Animal animal = Animal(); | ||
final HomeAnimal homeAnimal = HomeAnimal(); | ||
final Cat cat = Cat(); | ||
final Dog dog = Dog(); | ||
|
||
void main() { | ||
final result = animal as HomeAnimal; | ||
final result = animal as Animal; // LINT | ||
final result = cat as HomeAnimal; // LINT | ||
final result = cat as Animal; // LINT | ||
final result = dog as HomeAnimal; // LINT | ||
final result = dog as Animal; // LINT | ||
final result = animal as Dog; | ||
final result = animal as Cat; | ||
final result = homeAnimal as Cat; | ||
final result = homeAnimal as Dog; | ||
final result = homeAnimal as dynamic; | ||
final String regular; | ||
final s2 = regular as String?; // LINT | ||
} | ||
} | ||
|
||
class Example3 { | ||
final myList = <int>[1, 2, 3]; | ||
|
||
void main() { | ||
final result = myList as List<int>; // LINT | ||
} | ||
} | ||
|
||
class Animal {} | ||
|
||
class HomeAnimal extends Animal {} | ||
|
||
class Dog extends HomeAnimal {} | ||
|
||
class Cat extends HomeAnimal {} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
# Avoid unnecessary type casts | ||
|
||
## Rule id | ||
|
||
avoid-unnecessary-type-casts | ||
|
||
## Description | ||
Warns about of unnecessary use of casting operators. | ||
vlkonoshenko marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
### Example | ||
|
||
```dart | ||
class Example { | ||
final myList = <int>[1, 2, 3]; | ||
|
||
void main() { | ||
final result = myList as List<int>; // LINT | ||
} | ||
} | ||
``` |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.