@@ -166,6 +166,27 @@ export default createRule({
166
166
) ;
167
167
}
168
168
169
+ /**
170
+ * Check if a given node is a negative index expression
171
+ *
172
+ * E.g. `s.slice(- <expr>)`, `s.substring(s.length - <expr>)`
173
+ *
174
+ * @param node The node to check.
175
+ * @param expectedIndexedNode The node which is expected as the receiver of index expression.
176
+ */
177
+ function isNegativeIndexExpression (
178
+ node : TSESTree . Node ,
179
+ expectedIndexedNode : TSESTree . Node ,
180
+ ) : boolean {
181
+ return (
182
+ ( node . type === AST_NODE_TYPES . UnaryExpression &&
183
+ node . operator === '-' ) ||
184
+ ( node . type === AST_NODE_TYPES . BinaryExpression &&
185
+ node . operator === '-' &&
186
+ isLengthExpression ( node . left , expectedIndexedNode ) )
187
+ ) ;
188
+ }
189
+
169
190
/**
170
191
* Check if a given node is the expression of the last index.
171
192
*
@@ -538,9 +559,10 @@ export default createRule({
538
559
}
539
560
540
561
const isEndsWith =
541
- callNode . arguments . length === 1 ||
542
- ( callNode . arguments . length === 2 &&
543
- isLengthExpression ( callNode . arguments [ 1 ] , node . object ) ) ;
562
+ ( callNode . arguments . length === 1 ||
563
+ ( callNode . arguments . length === 2 &&
564
+ isLengthExpression ( callNode . arguments [ 1 ] , node . object ) ) ) &&
565
+ isNegativeIndexExpression ( callNode . arguments [ 0 ] , node . object ) ;
544
566
const isStartsWith =
545
567
! isEndsWith &&
546
568
callNode . arguments . length === 2 &&
@@ -564,6 +586,9 @@ export default createRule({
564
586
) {
565
587
return null ;
566
588
}
589
+ // code being checked is likely mistake:
590
+ // unequal length of strings being checked for equality
591
+ // or reliant on behavior of substring (negative indices interpreted as 0)
567
592
if ( isStartsWith ) {
568
593
if ( ! isLengthExpression ( callNode . arguments [ 1 ] , eqNode . right ) ) {
569
594
return null ;
0 commit comments