This repository has been archived by the owner on Jul 16, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 258
/
visitor.dart
119 lines (94 loc) · 3.03 KB
/
visitor.dart
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
// ignore_for_file: deprecated_member_use
part of 'prefer_iterable_of_rule.dart';
class _Visitor extends RecursiveAstVisitor<void> {
final _expressions = <InstanceCreationExpression>[];
Iterable<InstanceCreationExpression> get expressions => _expressions;
@override
void visitInstanceCreationExpression(InstanceCreationExpression node) {
super.visitInstanceCreationExpression(node);
if (isIterableOrSubclass(node.staticType) &&
node.constructorName.name?.name == 'from') {
final arg = node.argumentList.arguments.first;
final argumentType = _getType(arg.staticType);
final castedType = _getType(node.staticType);
if (argumentType != null &&
!argumentType.isDartCoreObject &&
!argumentType.isDynamic &&
_isUnnecessaryTypeCheck(castedType, argumentType)) {
_expressions.add(node);
}
}
}
DartType? _getType(DartType? type) {
if (type == null || type is! InterfaceType) {
return null;
}
final typeArgument = type.typeArguments.firstOrNull;
if (typeArgument == null) {
return null;
}
return typeArgument;
}
bool _isUnnecessaryTypeCheck(
DartType? objectType,
DartType? castedType,
) {
if (objectType == null || castedType == null) {
return false;
}
if (objectType == castedType) {
return true;
}
if (_checkNullableCompatibility(objectType, castedType)) {
return false;
}
final objectCastedType =
_foundCastedTypeInObjectTypeHierarchy(objectType, castedType);
if (objectCastedType == null) {
return true;
}
if (!_checkGenerics(objectCastedType, castedType)) {
return false;
}
return false;
}
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;
}
final length = objectType.typeArguments.length;
if (length != castedType.typeArguments.length) {
return false;
}
for (var argumentIndex = 0; argumentIndex < length; argumentIndex++) {
if (!_isUnnecessaryTypeCheck(
objectType.typeArguments[argumentIndex],
castedType.typeArguments[argumentIndex],
)) {
return false;
}
}
return true;
}
}