Skip to content

Commit 29f6b45

Browse files
authored
no-for-loop: Ignore known non-array loop variables (#1242)
1 parent 866c4a3 commit 29f6b45

File tree

2 files changed

+47
-8
lines changed

2 files changed

+47
-8
lines changed

rules/no-for-loop.js

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
'use strict';
2-
const {isClosingParenToken} = require('eslint-utils');
2+
const {isClosingParenToken, getStaticValue} = require('eslint-utils');
33
const getDocumentationUrl = require('./utils/get-documentation-url');
44
const isLiteralValue = require('./utils/is-literal-value');
55
const avoidCapture = require('./utils/avoid-capture');
@@ -60,7 +60,7 @@ const getStrictComparisonOperands = binaryExpression => {
6060
}
6161
};
6262

63-
const getArrayIdentifierNameFromBinaryExpression = (binaryExpression, indexIdentifierName) => {
63+
const getArrayIdentifierFromBinaryExpression = (binaryExpression, indexIdentifierName) => {
6464
const operands = getStrictComparisonOperands(binaryExpression);
6565

6666
if (!operands) {
@@ -88,17 +88,17 @@ const getArrayIdentifierNameFromBinaryExpression = (binaryExpression, indexIdent
8888
return;
8989
}
9090

91-
return greater.object.name;
91+
return greater.object;
9292
};
9393

94-
const getArrayIdentifierName = (forStatement, indexIdentifierName) => {
94+
const getArrayIdentifier = (forStatement, indexIdentifierName) => {
9595
const {test} = forStatement;
9696

9797
if (!test || test.type !== 'BinaryExpression') {
9898
return;
9999
}
100100

101-
return getArrayIdentifierNameFromBinaryExpression(test, indexIdentifierName);
101+
return getArrayIdentifierFromBinaryExpression(test, indexIdentifierName);
102102
};
103103

104104
const isLiteralOnePlusIdentifierWithName = (node, identifierName) => {
@@ -281,9 +281,17 @@ const create = context => {
281281
return;
282282
}
283283

284-
const arrayIdentifierName = getArrayIdentifierName(node, indexIdentifierName);
284+
const arrayIdentifier = getArrayIdentifier(node, indexIdentifierName);
285+
if (!arrayIdentifier) {
286+
return;
287+
}
288+
289+
const arrayIdentifierName = arrayIdentifier.name;
285290

286-
if (!arrayIdentifierName) {
291+
const scope = context.getScope();
292+
const staticResult = getStaticValue(arrayIdentifier, scope);
293+
if (staticResult && !Array.isArray(staticResult.value)) {
294+
// Bail out if we can tell that the array variable has a non-array value (i.e. we're looping through the characters of a string constant).
287295
return;
288296
}
289297

test/no-for-loop.mjs

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,12 @@ ruleTester.run('no-for-loop', rule, {
193193
console.log(cities)
194194
}
195195
}
196-
`
196+
`,
197+
198+
// With variable containing static, non-array value.
199+
'const notArray = "abc"; for (let i = 0; i < notArray.length; i++) { console.log(notArray[i]); }',
200+
'const notArray = 123; for (let i = 0; i < notArray.length; i++) { console.log(notArray[i]); }',
201+
'const notArray = true; for (let i = 0; i < notArray.length; i++) { console.log(notArray[i]); }'
197202
],
198203

199204
invalid: [
@@ -711,6 +716,32 @@ ruleTester.run('no-for-loop', rule, {
711716
for (const [i, city] of cities.entries()) {
712717
console.log(i, city);
713718
}
719+
`),
720+
721+
// With static array variable.
722+
testCase(outdent`
723+
const someArray = [1,2,3];
724+
for (let i = 0; i < someArray.length; i++) {
725+
console.log(someArray[i]);
726+
}
727+
`, outdent`
728+
const someArray = [1,2,3];
729+
for (const element of someArray) {
730+
console.log(element);
731+
}
732+
`),
733+
734+
// With non-static variable.
735+
testCase(outdent`
736+
const someArray = getSomeArray();
737+
for (let i = 0; i < someArray.length; i++) {
738+
console.log(someArray[i]);
739+
}
740+
`, outdent`
741+
const someArray = getSomeArray();
742+
for (const element of someArray) {
743+
console.log(element);
744+
}
714745
`)
715746
]
716747
});

0 commit comments

Comments
 (0)