OLD | NEW |
| (Empty) |
1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file | |
2 // for details. All rights reserved. Use of this source code is governed by a | |
3 // BSD-style license that can be found in the LICENSE file. | |
4 | |
5 library linter.src.rules.dont_compare_unrelated_types_for_equality; | |
6 | |
7 import 'package:analyzer/dart/ast/ast.dart'; | |
8 import 'package:analyzer/dart/ast/token.dart'; | |
9 import 'package:analyzer/dart/ast/visitor.dart'; | |
10 import 'package:analyzer/dart/element/element.dart'; | |
11 import 'package:analyzer/dart/element/type.dart'; | |
12 import 'package:linter/src/linter.dart'; | |
13 | |
14 const String _desc = r'Equality operator (==) invocation with references of' | |
15 r' unrelated types.'; | |
16 | |
17 const String _details = r''' | |
18 | |
19 **DON'T** Compare references of unrelated types for equality. | |
20 Comparing references of a type where neither is a subtype of the other most | |
21 likely will return false and might not reflect programmer's intent. | |
22 | |
23 **BAD:** | |
24 ``` | |
25 void someFunction() { | |
26 var x = '1'; | |
27 if (x == 1) print('someFunction'); // LINT | |
28 } | |
29 ``` | |
30 | |
31 **BAD:** | |
32 ``` | |
33 void someFunction1() { | |
34 String x = '1'; | |
35 if (x == 1) print('someFunction1'); // LINT | |
36 } | |
37 ``` | |
38 | |
39 **BAD:** | |
40 ``` | |
41 void someFunction13(DerivedClass2 instance) { | |
42 var other = new DerivedClass3(); | |
43 | |
44 if (other == instance) print('someFunction13'); // LINT | |
45 } | |
46 | |
47 class ClassBase {} | |
48 | |
49 class DerivedClass1 extends ClassBase {} | |
50 | |
51 abstract class Mixin {} | |
52 | |
53 class DerivedClass2 extends ClassBase with Mixin {} | |
54 | |
55 class DerivedClass3 extends ClassBase implements Mixin {} | |
56 ``` | |
57 | |
58 **GOOD:** | |
59 ``` | |
60 void someFunction2() { | |
61 var x = '1'; | |
62 var y = '2'; | |
63 if (x == y) print(someFunction2); // OK | |
64 } | |
65 ``` | |
66 | |
67 **GOOD:** | |
68 ``` | |
69 void someFunction3() { | |
70 for (var i = 0; i < 10; i++) { | |
71 if (i == 0) print(someFunction3); // OK | |
72 } | |
73 } | |
74 ``` | |
75 | |
76 **GOOD:** | |
77 ``` | |
78 void someFunction4() { | |
79 var x = '1'; | |
80 if (x == null) print(someFunction4); // OK | |
81 } | |
82 ``` | |
83 | |
84 **GOOD:** | |
85 ``` | |
86 void someFunction7() { | |
87 List someList; | |
88 | |
89 if (someList.length == 0) print('someFunction7'); // OK | |
90 } | |
91 ``` | |
92 | |
93 **GOOD:** | |
94 ``` | |
95 void someFunction8(ClassBase instance) { | |
96 DerivedClass1 other; | |
97 | |
98 if (other == instance) print('someFunction8'); // OK | |
99 } | |
100 ``` | |
101 | |
102 **GOOD:** | |
103 ``` | |
104 void someFunction10(unknown) { | |
105 var what = unknown - 1; | |
106 for (var index = 0; index < unknown; index++) { | |
107 if (what == index) print('someFunction10'); // OK | |
108 } | |
109 } | |
110 ``` | |
111 | |
112 **GOOD:** | |
113 ``` | |
114 void someFunction11(Mixin instance) { | |
115 var other = new DerivedClass2(); | |
116 | |
117 if (other == instance) print('someFunction11'); // OK | |
118 if (other != instance) print('!someFunction11'); // OK | |
119 } | |
120 | |
121 class ClassBase {} | |
122 | |
123 abstract class Mixin {} | |
124 | |
125 class DerivedClass2 extends ClassBase with Mixin {} | |
126 ``` | |
127 '''; | |
128 | |
129 class DontCompareUnrelatedTypesForEquality extends LintRule { | |
130 _Visitor _visitor; | |
131 | |
132 DontCompareUnrelatedTypesForEquality() : super( | |
133 name: 'dont_compare_unrelated_types_for_equality', | |
134 description: _desc, | |
135 details: _details, | |
136 group: Group.errors, | |
137 maturity: Maturity.experimental) { | |
138 _visitor = new _Visitor(this); | |
139 } | |
140 | |
141 @override | |
142 AstVisitor getVisitor() => _visitor; | |
143 } | |
144 | |
145 class _Visitor extends SimpleAstVisitor { | |
146 static const String _dartCoreLibraryName = 'dart.core'; | |
147 static const String _boolClassName = 'bool'; | |
148 | |
149 final LintRule rule; | |
150 | |
151 _Visitor(this.rule); | |
152 | |
153 @override | |
154 void visitBinaryExpression(BinaryExpression node) { | |
155 bool isDartCoreBoolean = node.bestType.name == _boolClassName && | |
156 node.bestType.element?.library?.name == _dartCoreLibraryName; | |
157 if (!isDartCoreBoolean || (node.operator.type != TokenType.EQ_EQ && | |
158 node.operator.type != TokenType.BANG_EQ)) { | |
159 return; | |
160 } | |
161 | |
162 if (_unrelatedTypes(node)) { | |
163 rule.reportLint(node); | |
164 } | |
165 } | |
166 | |
167 bool _unrelatedTypes(BinaryExpression node) { | |
168 DartType leftType = node.leftOperand.bestType; | |
169 DartType rightType = node.rightOperand.bestType; | |
170 if (leftType.isBottom || leftType.isDynamic || rightType.isBottom || rightTy
pe.isDynamic) { | |
171 return false; | |
172 } | |
173 if (leftType == rightType || | |
174 leftType.isMoreSpecificThan(rightType) || | |
175 rightType.isMoreSpecificThan(leftType)) { | |
176 return false; | |
177 } | |
178 Element leftElement = leftType.element; | |
179 Element rightElement = rightType.element; | |
180 if (leftElement is ClassElement && rightElement is ClassElement) { | |
181 return leftElement.supertype.isObject || | |
182 leftElement.supertype != rightElement.supertype; | |
183 } | |
184 return false; | |
185 } | |
186 } | |
OLD | NEW |