diff --git a/docs/rules/no-array-for-each.md b/docs/rules/no-array-for-each.md new file mode 100644 index 0000000000..c0dd2986d2 --- /dev/null +++ b/docs/rules/no-array-for-each.md @@ -0,0 +1,45 @@ +# Prefer `for…of` over `Array#forEach(…)` + +A [`for…of` statement](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...of) is more readable than [`Array#forEach(…)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach). + +This rule is partly fixable. + +## Fail + +```js +array.forEach(element => { + bar(element); +}); +``` + +```js +array.forEach((element, index) => { + bar(element, index); +}); +``` + +```js +array.forEach((element, index, array) => { + bar(element, index, array); +}); +``` + +## Pass + +```js +for (const element of array) { + bar(element); +} +``` + +```js +for (const [index, element] of array.entries()) { + bar(element, index); +} +``` + +```js +for (const [index, element] of array.entries()) { + bar(element, index, array); +} +``` diff --git a/index.js b/index.js index e36c410b4f..d3247f14f6 100644 --- a/index.js +++ b/index.js @@ -55,6 +55,7 @@ module.exports = { 'unicorn/new-for-builtins': 'error', 'unicorn/no-abusive-eslint-disable': 'error', 'unicorn/no-array-callback-reference': 'error', + 'unicorn/no-array-for-each': 'error', 'unicorn/no-array-push-push': 'error', 'unicorn/no-array-reduce': 'error', 'unicorn/no-console-spaces': 'error', diff --git a/readme.md b/readme.md index d933b45f22..11b54e5d3e 100644 --- a/readme.md +++ b/readme.md @@ -49,6 +49,7 @@ Configure it in `package.json`. "unicorn/new-for-builtins": "error", "unicorn/no-abusive-eslint-disable": "error", "unicorn/no-array-callback-reference": "error", + "unicorn/no-array-for-each": "error", "unicorn/no-array-push-push": "error", "unicorn/no-array-reduce": "error", "unicorn/no-console-spaces": "error", @@ -127,6 +128,7 @@ Configure it in `package.json`. - [new-for-builtins](docs/rules/new-for-builtins.md) - Enforce the use of `new` for all builtins, except `String`, `Number`, `Boolean`, `Symbol` and `BigInt`. *(partly fixable)* - [no-abusive-eslint-disable](docs/rules/no-abusive-eslint-disable.md) - Enforce specifying rules to disable in `eslint-disable` comments. - [no-array-callback-reference](docs/rules/no-array-callback-reference.md) - Prevent passing a function reference directly to iterator methods. +- [no-array-for-each](docs/rules/no-array-for-each.md) - Prefer `for…of` over `Array#forEach(…)`. *(partly fixable)* - [no-array-push-push](docs/rules/no-array-push-push.md) - Enforce combining multiple `Array#push()` into one call. *(partly fixable)* - [no-array-reduce](docs/rules/no-array-reduce.md) - Disallow `Array#reduce()` and `Array#reduceRight()`. - [no-console-spaces](docs/rules/no-console-spaces.md) - Do not use leading/trailing space between `console.log` parameters. *(fixable)* diff --git a/rules/no-array-for-each.js b/rules/no-array-for-each.js new file mode 100644 index 0000000000..cdd5afae54 --- /dev/null +++ b/rules/no-array-for-each.js @@ -0,0 +1,396 @@ +'use strict'; +const { + isParenthesized, + isArrowToken, + isCommaToken, + isSemicolonToken, + isClosingParenToken, + findVariable +} = require('eslint-utils'); +const getDocumentationUrl = require('./utils/get-documentation-url'); +const methodSelector = require('./utils/method-selector'); +const needsSemicolon = require('./utils/needs-semicolon'); +const shouldAddParenthesesToExpressionStatementExpression = require('./utils/should-add-parentheses-to-expression-statement-expression'); +const getParenthesizedTimes = require('./utils/get-parenthesized-times'); +const extendFixRange = require('./utils/extend-fix-range'); + +const MESSAGE_ID = 'no-array-for-each'; +const messages = { + [MESSAGE_ID]: 'Do not use `Array#forEach(…)`.' +}; + +const arrayForEachCallSelector = methodSelector({ + name: 'forEach', + includeOptional: true +}); + +const continueAbleNodeTypes = new Set([ + 'WhileStatement', + 'DoWhileStatement', + 'ForStatement', + 'ForOfStatement', + 'ForInStatement' +]); + +function isReturnStatementInContinueAbleNodes(returnStatement, callbackFunction) { + for (let node = returnStatement; node && node !== callbackFunction; node = node.parent) { + if (continueAbleNodeTypes.has(node.type)) { + return true; + } + } + + return false; +} + +function getFixFunction(callExpression, sourceCode, functionInfo) { + const [callback] = callExpression.arguments; + const parameters = callback.params; + const array = callExpression.callee.object; + const {returnStatements} = functionInfo.get(callback); + + const getForOfLoopHeadText = () => { + const parametersText = parameters.map(parameter => sourceCode.getText(parameter)); + const useEntries = parameters.length === 2; + + let text = 'for (const '; + text += useEntries ? `[${parametersText.join(', ')}]` : parametersText[0]; + + text += ' of '; + + let arrayText = sourceCode.getText(array); + if (isParenthesized(array, sourceCode)) { + arrayText = `(${arrayText})`; + } + + text += arrayText; + + if (useEntries) { + text += '.entries()'; + } + + text += ') '; + + return text; + }; + + const getForOfLoopHeadRange = () => { + const [start] = callExpression.range; + let end; + if (callback.body.type === 'BlockStatement') { + end = callback.body.range[0]; + } else { + const arrowToken = sourceCode.getTokenBefore(callback.body, isArrowToken); + end = arrowToken.range[1]; + } + + return [start, end]; + }; + + function * replaceReturnStatement(returnStatement, fixer) { + const returnToken = sourceCode.getFirstToken(returnStatement); + + /* istanbul ignore next: `ReturnStatement` firstToken should be `return` */ + if (returnToken.value !== 'return') { + throw new Error(`Unexpected token ${returnToken.value}.`); + } + + if (!returnStatement.argument) { + yield fixer.replaceText(returnToken, 'continue'); + return; + } + + // Remove `return` + yield fixer.remove(returnToken); + + const previousToken = sourceCode.getTokenBefore(returnToken); + const nextToken = sourceCode.getTokenAfter(returnToken); + let textBefore = ''; + let textAfter = ''; + const shouldAddParentheses = + !isParenthesized(returnStatement.argument, sourceCode) && + shouldAddParenthesesToExpressionStatementExpression(returnStatement.argument); + if (shouldAddParentheses) { + textBefore = '('; + textAfter = ')'; + } + + const shouldAddSemicolonBefore = needsSemicolon(previousToken, sourceCode, shouldAddParentheses ? '(' : nextToken.value); + if (shouldAddSemicolonBefore) { + textBefore = `;${textBefore}`; + } + + if (textBefore) { + yield fixer.insertTextBefore(nextToken, textBefore); + } + + if (textAfter) { + yield fixer.insertTextAfter(returnStatement.argument, textAfter); + } + + // If `returnStatement` has no semi + const lastToken = sourceCode.getLastToken(returnStatement); + yield fixer.insertTextAfter( + returnStatement, + `${isSemicolonToken(lastToken) ? '' : ';'} continue;` + ); + } + + const shouldRemoveExpressionStatementLastToken = token => { + if (!isSemicolonToken(token)) { + return false; + } + + if (callback.body.type !== 'BlockStatement') { + return false; + } + + return true; + }; + + function * removeCallbackParentheses(fixer) { + const parenthesizedTimes = getParenthesizedTimes(callback, sourceCode); + if (parenthesizedTimes > 0) { + // Opening parenthesis tokens already included in `getForOfLoopHeadRange` + + const closingParenthesisTokens = sourceCode.getTokensAfter( + callback, + {count: parenthesizedTimes, filter: isClosingParenToken} + ); + + for (const closingParenthesisToken of closingParenthesisTokens) { + yield fixer.remove(closingParenthesisToken); + } + } + } + + return function * (fixer) { + yield fixer.replaceTextRange(getForOfLoopHeadRange(), getForOfLoopHeadText()); + + yield * removeCallbackParentheses(fixer); + + // Remove call expression trailing comma + const [ + penultimateToken, + lastToken + ] = sourceCode.getLastTokens(callExpression, 2); + + if (isCommaToken(penultimateToken)) { + yield fixer.remove(penultimateToken); + } + + yield fixer.remove(lastToken); + + for (const returnStatement of returnStatements) { + yield * replaceReturnStatement(returnStatement, fixer); + } + + const expressionStatementLastToken = sourceCode.getLastToken(callExpression.parent); + if (shouldRemoveExpressionStatementLastToken(expressionStatementLastToken)) { + yield fixer.remove(expressionStatementLastToken, fixer); + } + + // Prevent possible conflicts + yield * extendFixRange(fixer, callExpression.parent.range); + }; +} + +const isChildScope = (child, parent) => { + for (let scope = child; scope; scope = scope.upper) { + if (scope === parent) { + return true; + } + } + + return false; +}; + +function isParameterSafeToFix(parameter, {scope, array, allIdentifiers}) { + const {type, name: parameterName} = parameter; + if (type !== 'Identifier') { + return false; + } + + const [arrayStart, arrayEnd] = array.range; + for (const identifier of allIdentifiers) { + const {name, range: [start, end]} = identifier; + if ( + name !== parameterName || + start < arrayStart || + end > arrayEnd + ) { + continue; + } + + const variable = findVariable(scope, identifier); + if (!variable || variable.scope === scope || isChildScope(scope, variable.scope)) { + return false; + } + } + + return true; +} + +function isFixable(callExpression, sourceCode, {scope, functionInfo, allIdentifiers}) { + // Check `CallExpression` + if ( + callExpression.optional || + isParenthesized(callExpression, sourceCode) || + callExpression.arguments.length !== 1 + ) { + return false; + } + + // Check `CallExpression.parent` + if (callExpression.parent.type !== 'ExpressionStatement') { + return false; + } + + // Check `CallExpression.callee` + /* istanbul ignore next: Because of `ChainExpression` wrapper, `foo?.forEach()` is already failed on previous check, keep this just for safety */ + if (callExpression.callee.optional) { + return false; + } + + // Check `CallExpression.arguments[0]`; + const [callback] = callExpression.arguments; + if ( + // Leave non-function type to `no-array-callback-reference` rule + (callback.type !== 'FunctionExpression' && callback.type !== 'ArrowFunctionExpression') || + callback.async || + callback.generator + ) { + return false; + } + + // Check `callback.params` + const parameters = callback.params; + if ( + !(parameters.length === 1 || parameters.length === 2) || + parameters.some(parameter => !isParameterSafeToFix(parameter, {scope, array: callExpression, allIdentifiers})) + ) { + return false; + } + + // `foo.forEach((element: Type, index: number) => bar())`, should fix to `for (const [index, element]: [number, Type] of …`, not handled + if (parameters.length === 2 && parameters.some(node => node.typeAnnotation)) { + return false; + } + + // Check `ReturnStatement`s in `callback` + const {returnStatements, thisFound, scope: callbackScope} = functionInfo.get(callback); + if (returnStatements.some(returnStatement => isReturnStatementInContinueAbleNodes(returnStatement, callback))) { + return false; + } + + // Check `callback` self + if (callback.type === 'FunctionExpression') { + if (thisFound) { + return false; + } + + const argumentsVariable = findVariable(callbackScope, 'arguments'); + if ( + argumentsVariable && + argumentsVariable.references.some(reference => reference.from === callbackScope) + ) { + return false; + } + + if (callback.id) { + const idVariable = findVariable(callbackScope, callback.id); + + if (idVariable && idVariable.references.length > 0) { + return false; + } + } + } + + return true; +} + +const create = context => { + const functionStack = []; + const nonArrowFunctionStack = []; + const callExpressions = []; + const allIdentifiers = []; + const functionInfo = new Map(); + + const sourceCode = context.getSourceCode(); + + return { + ':function'(node) { + functionStack.push(node); + functionInfo.set(node, { + returnStatements: [], + thisFound: false, + scope: context.getScope() + }); + + if (node.type !== 'ArrowFunctionExpression') { + nonArrowFunctionStack.push(node); + } + }, + ':function:exit'(node) { + functionStack.pop(); + + if (node.type !== 'ArrowFunctionExpression') { + nonArrowFunctionStack.pop(); + } + }, + ThisExpression() { + const currentNonArrowFunction = nonArrowFunctionStack[functionStack.length - 1]; + if (!currentNonArrowFunction) { + return; + } + + const currentFunctionInfo = functionInfo.get(currentNonArrowFunction); + currentFunctionInfo.thisFound = true; + }, + Identifier(node) { + allIdentifiers.push(node); + }, + ReturnStatement(node) { + const currentFunction = functionStack[functionStack.length - 1]; + // `globalReturn ` + /* istanbul ignore next: ESLint deprecated `ecmaFeatures`, can't test */ + if (!currentFunction) { + return; + } + + const {returnStatements} = functionInfo.get(currentFunction); + returnStatements.push(node); + }, + [arrayForEachCallSelector](node) { + callExpressions.push({ + node, + scope: context.getScope() + }); + }, + 'Program:exit'() { + for (const {node, scope} of callExpressions) { + const problem = { + node: node.callee.property, + messageId: MESSAGE_ID + }; + + if (isFixable(node, sourceCode, {scope, allIdentifiers, functionInfo})) { + problem.fix = getFixFunction(node, sourceCode, functionInfo); + } + + context.report(problem); + } + } + }; +}; + +module.exports = { + create, + meta: { + type: 'suggestion', + docs: { + url: getDocumentationUrl(__filename) + }, + fixable: 'code', + messages + } +}; diff --git a/rules/no-unused-properties.js b/rules/no-unused-properties.js index aa22ed79a3..3eea993f64 100644 --- a/rules/no-unused-properties.js +++ b/rules/no-unused-properties.js @@ -118,15 +118,15 @@ const create = context => { }; const checkProperties = (objectExpression, references, path = []) => { - objectExpression.properties.forEach(property => { + for (const property of objectExpression.properties) { const {key} = property; if (!key) { - return; + continue; } if (propertyKeysEqual(key, specialProtoPropertyKey)) { - return; + continue; } const nextPath = path.concat(key); @@ -187,7 +187,7 @@ const create = context => { .filter(Boolean); checkProperty(property, nextReferences, nextPath); - }); + } }; const checkObject = (declaratorOrProperty, references, path) => { @@ -215,11 +215,15 @@ const create = context => { }; const checkVariables = scope => { - scope.variables.forEach(variable => checkVariable(variable)); + for (const variable of scope.variables) { + checkVariable(variable); + } }; const checkChildScopes = scope => { - scope.childScopes.forEach(scope => checkScope(scope)); + for (const childScope of scope.childScopes) { + checkScope(childScope); + } }; const checkScope = scope => { diff --git a/rules/prevent-abbreviations.js b/rules/prevent-abbreviations.js index 1af88d2f3e..041bd6ebcb 100644 --- a/rules/prevent-abbreviations.js +++ b/rules/prevent-abbreviations.js @@ -673,11 +673,15 @@ const create = context => { }; const checkVariables = scope => { - scope.variables.forEach(variable => checkPossiblyWeirdClassVariable(variable)); + for (const variable of scope.variables) { + checkPossiblyWeirdClassVariable(variable); + } }; const checkChildScopes = scope => { - scope.childScopes.forEach(scope => checkScope(scope)); + for (const childScope of scope.childScopes) { + checkScope(childScope); + } }; const checkScope = scope => { diff --git a/rules/utils/method-selector.js b/rules/utils/method-selector.js index aa78bad0b3..0db24003b9 100644 --- a/rules/utils/method-selector.js +++ b/rules/utils/method-selector.js @@ -8,7 +8,8 @@ module.exports = options => { object, min, max, - property = '' + property = '', + includeOptional = false } = { min: 0, max: Number.POSITIVE_INFINITY, @@ -20,10 +21,13 @@ module.exports = options => { const selector = [ `[${prefix}type="CallExpression"]`, `[${prefix}callee.type="MemberExpression"]`, - `[${prefix}callee.computed=false]`, `[${prefix}callee.property.type="Identifier"]` ]; + if (!includeOptional) { + selector.push(`[${prefix}callee.computed=false]`); + } + if (name) { selector.push(`[${prefix}callee.property.name="${name}"]`); } diff --git a/rules/utils/should-add-parentheses-to-expression-statement-expression.js b/rules/utils/should-add-parentheses-to-expression-statement-expression.js new file mode 100644 index 0000000000..3bd6122158 --- /dev/null +++ b/rules/utils/should-add-parentheses-to-expression-statement-expression.js @@ -0,0 +1,21 @@ +'use strict'; + +/** +Check if parentheses should to be added to a `node` when it's used as an `expression` of `ExpressionStatement`. + +@param {Node} node - The AST node to check. +@param {SourceCode} sourceCode - The source code object. +@returns {boolean} +*/ +function shouldAddParenthesesToExpressionStatementExpression(node) { + switch (node.type) { + case 'ObjectExpression': + return true; + case 'AssignmentExpression': + return node.left.type === 'ObjectPattern' || node.left.type === 'ArrayPattern'; + default: + return false; + } +} + +module.exports = shouldAddParenthesesToExpressionStatementExpression; diff --git a/test/integration/fixtures-local/conflicts-no-array-for-each-and-prevent-abbreviations.js b/test/integration/fixtures-local/conflicts-no-array-for-each-and-prevent-abbreviations.js new file mode 100644 index 0000000000..0383a2dd38 --- /dev/null +++ b/test/integration/fixtures-local/conflicts-no-array-for-each-and-prevent-abbreviations.js @@ -0,0 +1,2 @@ +const btn = bar; +foo.forEach(btn => click(btn)) diff --git a/test/no-array-for-each.js b/test/no-array-for-each.js new file mode 100644 index 0000000000..7cca704672 --- /dev/null +++ b/test/no-array-for-each.js @@ -0,0 +1,372 @@ +import {outdent} from 'outdent'; +import {test} from './utils/test.js'; + +test.snapshot({ + valid: [ + 'new foo.forEach(element => bar())', + 'forEach(element => bar())', + 'foo.notForEach(element => bar())' + ], + invalid: [ + // Not fixable + 'foo.forEach?.(element => bar(element))', + '(foo.forEach(element => bar(element)))', + 'foo.forEach(element => bar(element), thisArgument)', + 'foo.forEach()', + 'const baz = foo.forEach(element => bar(element))', + 'foo?.forEach(element => bar(element))', + 'foo.forEach(bar)', + 'foo.forEach(async function(element) {})', + 'foo.forEach(function * (element) {})', + 'foo.forEach(() => bar())', + 'foo.forEach((element, index, array) => bar())', + // Ideally this should be fixable, but hard to know variable conflicts + 'foo.forEach(({property}) => bar(property))', + + // Can't turn `return` to `continue` + outdent` + foo.forEach(element => { + do { + return + } while (element) + }); + `, + outdent` + foo.forEach(element => { + while (element) { + return; + } + }); + `, + outdent` + foo.forEach(element => { + for (let i = 0; i < 2; i++) { + return; + } + }); + `, + outdent` + foo.forEach(element => { + for (let i in element) { + return; + } + }); + `, + outdent` + foo.forEach(element => { + for (let i of element) { + return; + } + }); + `, + // `ReturnStatement` in `switch` is fixable + outdent` + foo.forEach(element => { + switch (element) { + case 1: + break; + case 2: + return; + } + }); + `, + + // `parameters` + 'foo.forEach(foo => bar());', + outdent` + const foo = []; + foo.forEach(foo => bar()); + `, + outdent` + const foo = []; + function unicorn() { + foo.forEach(foo => bar()); + } + `, + 'index.forEach((a, index) => bar());', + outdent` + const index = []; + index.forEach((a, index) => bar()); + `, + outdent` + const index = []; + function unicorn() { + index.forEach((a, index) => bar()); + } + `, + 'a[foo].forEach(foo => bar());', + outdent` + const foo = 1; + a[foo].forEach(foo => bar()); + `, + outdent` + const foo = 1; + function unicorn() { + a[foo].forEach(foo => bar()); + } + `, + 'a[index].forEach((b, index) => bar());', + 'a((foo) => foo).forEach(foo => bar());', + 'a((foo, index) => foo + index).forEach((foo, index) => bar());', + outdent` + const foo = []; + const index = 1; + a.forEach((foo, index) => foo[index]); + `, + + // `FunctionExpression.id` + outdent` + foo.forEach(function a(element) { + bar(a) + }) + `, + outdent` + foo.forEach(function a(element) { + function b() { + bar(a) + } + }) + `, + outdent` + foo.forEach(function a(element) { + function b(a) { + bar(a) + } + }) + `, + + // This + outdent` + foo.forEach(function(element) { + bar(this) + }) + `, + outdent` + foo.forEach(function(element) { + function b() { + bar(this) + } + }) + `, + outdent` + foo.forEach(function(element) { + const x = b => { + bar(this) + } + }) + `, + outdent` + foo.forEach((element) => { + bar(this) + }) + `, + + // `arguments` + outdent` + foo.forEach(function(element) { + bar(arguments) + }) + `, + outdent` + foo.forEach(function(element) { + function b() { + bar(arguments) + } + }) + `, + outdent` + foo.forEach(function(element) { + const b = () => { + bar(arguments) + } + }) + `, + outdent` + foo.forEach((element) => { + bar(arguments) + }) + `, + + // Auto-fix + outdent` + foo.forEach(function (element) { + bar(element); + }); + `, + outdent` + foo.forEach(function withName(element) { + bar(element); + }); + `, + outdent` + foo.forEach((element) => { + bar(element); + }); + `, + 'foo.forEach((element) => bar(element));', + outdent` + foo.forEach(function (element, index) { + bar(element, index); + }); + `, + outdent` + foo.forEach(function withName(element, index) { + bar(element, index); + }); + `, + outdent` + foo.forEach((element, index) => { + bar(element, index); + }); + `, + 'foo.forEach((element, index) => bar(element, index));', + // Array is parenthesized + '(foo).forEach((element, index) => bar(element, index))', + '(0, foo).forEach((element, index) => bar(element, index))', + // Trailing comma + outdent` + foo.forEach(function (element) { + bar(element); + },); + `, + outdent` + foo.forEach(function withName(element) { + bar(element); + },); + `, + outdent` + foo.forEach((element) => { + bar(element); + },); + `, + 'foo.forEach((element) => bar(element),);', + // Last semi token + outdent` + foo.forEach((element) => bar(element)) + ;[foo].pop(); + `, + outdent` + foo.forEach((element) => { + bar(element); + }); + function noneRelatedFunction() { + while (element) { + return; + } + } + `, + // A test to make sure function head part range correctly calculated + 'foo.forEach(element => ({}))', + // `callback` is parenthesized + 'foo.forEach((((((element => bar(element)))))));', + outdent` + foo.forEach((element) => { + if (1) { + return; + } + if (1) { + return + } + if (1) { + return!true; + } + if (1) { + return!true + } + if (1) { + return bar(); + } + if (1) { + return bar() + unreachable(); + } + if (1) { + return {}; + } + if (1) { + return ({}); + } + if (1) { + return {a} = a; + } + if (1) { + return [a] = a; + } + if (1) { + foo + return [] + } + if (1) { + foo + return [foo] = bar; + } + }); + ` + ] +}); + +test.typescript({ + valid: [], + invalid: [ + // https://github.com/vercel/next.js/blob/699a7aeaaa48a6c3611ede7a35af2d9676421de0/packages/next/build/index.ts#L1358 + { + code: outdent` + staticPages.forEach((pg) => allStaticPages.add(pg)) + pageInfos.forEach((info: PageInfo, key: string) => { + allPageInfos.set(key, info) + }) + `, + output: outdent` + for (const pg of staticPages) allStaticPages.add(pg) + pageInfos.forEach((info: PageInfo, key: string) => { + allPageInfos.set(key, info) + }) + `, + errors: 2 + }, + // https://github.com/gatsbyjs/gatsby/blob/3163ca67d44b79c727dd3e331fb56b21707877a5/packages/gatsby/src/bootstrap/remove-stale-jobs.ts#L14 + { + code: outdent` + const actions: Array = [] + + state.jobsV2.complete.forEach( + (job: IGatsbyCompleteJobV2, contentDigest: string): void => { + if (isJobStale(job)) { + actions.push(internalActions.removeStaleJob(contentDigest)) + } + } + ) + `, + output: outdent` + const actions: Array = [] + + state.jobsV2.complete.forEach( + (job: IGatsbyCompleteJobV2, contentDigest: string): void => { + if (isJobStale(job)) { + actions.push(internalActions.removeStaleJob(contentDigest)) + } + } + ) + `, + errors: 1 + }, + // https://github.com/microsoft/fluentui/blob/20f3d664a36c93174dc32786a9d465dd343dabe5/apps/todo-app/src/DataProvider.ts#L157 + { + code: 'this._listeners.forEach((listener: () => void) => listener());', + output: 'for (const listener: () => void of this._listeners) listener();', + errors: 1 + }, + // https://github.com/angular/angular/blob/4e8198d60f421ce120e3a6b57afe60a9332d2692/packages/animations/browser/src/render/transition_animation_engine.ts#L1636 + { + code: outdent` + const cloakVals: string[] = []; + elements.forEach(element => cloakVals.push(cloakElement(element))); + `, + output: outdent` + const cloakVals: string[] = []; + for (const element of elements) cloakVals.push(cloakElement(element)); + `, + errors: 1 + } + ] +}); diff --git a/test/snapshots/no-array-for-each.js.md b/test/snapshots/no-array-for-each.js.md new file mode 100644 index 0000000000..7eb9b1d409 --- /dev/null +++ b/test/snapshots/no-array-for-each.js.md @@ -0,0 +1,1176 @@ +# Snapshot report for `test/no-array-for-each.js` + +The actual snapshot is saved in `no-array-for-each.js.snap`. + +Generated by [AVA](https://avajs.dev). + +## Invalid #1 + 1 | foo.forEach?.(element => bar(element)) + +> Error 1/1 + + `␊ + > 1 | foo.forEach?.(element => bar(element))␊ + | ^^^^^^^ Do not use `Array#forEach(…)`.␊ + ` + +## Invalid #2 + 1 | (foo.forEach(element => bar(element))) + +> Error 1/1 + + `␊ + > 1 | (foo.forEach(element => bar(element)))␊ + | ^^^^^^^ Do not use `Array#forEach(…)`.␊ + ` + +## Invalid #3 + 1 | foo.forEach(element => bar(element), thisArgument) + +> Error 1/1 + + `␊ + > 1 | foo.forEach(element => bar(element), thisArgument)␊ + | ^^^^^^^ Do not use `Array#forEach(…)`.␊ + ` + +## Invalid #4 + 1 | foo.forEach() + +> Error 1/1 + + `␊ + > 1 | foo.forEach()␊ + | ^^^^^^^ Do not use `Array#forEach(…)`.␊ + ` + +## Invalid #5 + 1 | const baz = foo.forEach(element => bar(element)) + +> Error 1/1 + + `␊ + > 1 | const baz = foo.forEach(element => bar(element))␊ + | ^^^^^^^ Do not use `Array#forEach(…)`.␊ + ` + +## Invalid #6 + 1 | foo?.forEach(element => bar(element)) + +> Error 1/1 + + `␊ + > 1 | foo?.forEach(element => bar(element))␊ + | ^^^^^^^ Do not use `Array#forEach(…)`.␊ + ` + +## Invalid #7 + 1 | foo.forEach(bar) + +> Error 1/1 + + `␊ + > 1 | foo.forEach(bar)␊ + | ^^^^^^^ Do not use `Array#forEach(…)`.␊ + ` + +## Invalid #8 + 1 | foo.forEach(async function(element) {}) + +> Error 1/1 + + `␊ + > 1 | foo.forEach(async function(element) {})␊ + | ^^^^^^^ Do not use `Array#forEach(…)`.␊ + ` + +## Invalid #9 + 1 | foo.forEach(function * (element) {}) + +> Error 1/1 + + `␊ + > 1 | foo.forEach(function * (element) {})␊ + | ^^^^^^^ Do not use `Array#forEach(…)`.␊ + ` + +## Invalid #10 + 1 | foo.forEach(() => bar()) + +> Error 1/1 + + `␊ + > 1 | foo.forEach(() => bar())␊ + | ^^^^^^^ Do not use `Array#forEach(…)`.␊ + ` + +## Invalid #11 + 1 | foo.forEach((element, index, array) => bar()) + +> Error 1/1 + + `␊ + > 1 | foo.forEach((element, index, array) => bar())␊ + | ^^^^^^^ Do not use `Array#forEach(…)`.␊ + ` + +## Invalid #12 + 1 | foo.forEach(({property}) => bar(property)) + +> Error 1/1 + + `␊ + > 1 | foo.forEach(({property}) => bar(property))␊ + | ^^^^^^^ Do not use `Array#forEach(…)`.␊ + ` + +## Invalid #13 + 1 | foo.forEach(element => { + 2 | do { + 3 | return + 4 | } while (element) + 5 | }); + +> Error 1/1 + + `␊ + > 1 | foo.forEach(element => {␊ + | ^^^^^^^ Do not use `Array#forEach(…)`.␊ + 2 | do {␊ + 3 | return␊ + 4 | } while (element)␊ + 5 | });␊ + ` + +## Invalid #14 + 1 | foo.forEach(element => { + 2 | while (element) { + 3 | return; + 4 | } + 5 | }); + +> Error 1/1 + + `␊ + > 1 | foo.forEach(element => {␊ + | ^^^^^^^ Do not use `Array#forEach(…)`.␊ + 2 | while (element) {␊ + 3 | return;␊ + 4 | }␊ + 5 | });␊ + ` + +## Invalid #15 + 1 | foo.forEach(element => { + 2 | for (let i = 0; i < 2; i++) { + 3 | return; + 4 | } + 5 | }); + +> Error 1/1 + + `␊ + > 1 | foo.forEach(element => {␊ + | ^^^^^^^ Do not use `Array#forEach(…)`.␊ + 2 | for (let i = 0; i < 2; i++) {␊ + 3 | return;␊ + 4 | }␊ + 5 | });␊ + ` + +## Invalid #16 + 1 | foo.forEach(element => { + 2 | for (let i in element) { + 3 | return; + 4 | } + 5 | }); + +> Error 1/1 + + `␊ + > 1 | foo.forEach(element => {␊ + | ^^^^^^^ Do not use `Array#forEach(…)`.␊ + 2 | for (let i in element) {␊ + 3 | return;␊ + 4 | }␊ + 5 | });␊ + ` + +## Invalid #17 + 1 | foo.forEach(element => { + 2 | for (let i of element) { + 3 | return; + 4 | } + 5 | }); + +> Error 1/1 + + `␊ + > 1 | foo.forEach(element => {␊ + | ^^^^^^^ Do not use `Array#forEach(…)`.␊ + 2 | for (let i of element) {␊ + 3 | return;␊ + 4 | }␊ + 5 | });␊ + ` + +## Invalid #18 + 1 | foo.forEach(element => { + 2 | switch (element) { + 3 | case 1: + 4 | break; + 5 | case 2: + 6 | return; + 7 | } + 8 | }); + +> Output + + `␊ + 1 | for (const element of foo) {␊ + 2 | switch (element) {␊ + 3 | case 1:␊ + 4 | break;␊ + 5 | case 2:␊ + 6 | continue;␊ + 7 | }␊ + 8 | }␊ + ` + +> Error 1/1 + + `␊ + > 1 | foo.forEach(element => {␊ + | ^^^^^^^ Do not use `Array#forEach(…)`.␊ + 2 | switch (element) {␊ + 3 | case 1:␊ + 4 | break;␊ + 5 | case 2:␊ + 6 | return;␊ + 7 | }␊ + 8 | });␊ + ` + +## Invalid #19 + 1 | foo.forEach(foo => bar()); + +> Error 1/1 + + `␊ + > 1 | foo.forEach(foo => bar());␊ + | ^^^^^^^ Do not use `Array#forEach(…)`.␊ + ` + +## Invalid #20 + 1 | const foo = []; + 2 | foo.forEach(foo => bar()); + +> Error 1/1 + + `␊ + 1 | const foo = [];␊ + > 2 | foo.forEach(foo => bar());␊ + | ^^^^^^^ Do not use `Array#forEach(…)`.␊ + ` + +## Invalid #21 + 1 | const foo = []; + 2 | function unicorn() { + 3 | foo.forEach(foo => bar()); + 4 | } + +> Error 1/1 + + `␊ + 1 | const foo = [];␊ + 2 | function unicorn() {␊ + > 3 | foo.forEach(foo => bar());␊ + | ^^^^^^^ Do not use `Array#forEach(…)`.␊ + 4 | }␊ + ` + +## Invalid #22 + 1 | index.forEach((a, index) => bar()); + +> Error 1/1 + + `␊ + > 1 | index.forEach((a, index) => bar());␊ + | ^^^^^^^ Do not use `Array#forEach(…)`.␊ + ` + +## Invalid #23 + 1 | const index = []; + 2 | index.forEach((a, index) => bar()); + +> Error 1/1 + + `␊ + 1 | const index = [];␊ + > 2 | index.forEach((a, index) => bar());␊ + | ^^^^^^^ Do not use `Array#forEach(…)`.␊ + ` + +## Invalid #24 + 1 | const index = []; + 2 | function unicorn() { + 3 | index.forEach((a, index) => bar()); + 4 | } + +> Error 1/1 + + `␊ + 1 | const index = [];␊ + 2 | function unicorn() {␊ + > 3 | index.forEach((a, index) => bar());␊ + | ^^^^^^^ Do not use `Array#forEach(…)`.␊ + 4 | }␊ + ` + +## Invalid #25 + 1 | a[foo].forEach(foo => bar()); + +> Error 1/1 + + `␊ + > 1 | a[foo].forEach(foo => bar());␊ + | ^^^^^^^ Do not use `Array#forEach(…)`.␊ + ` + +## Invalid #26 + 1 | const foo = 1; + 2 | a[foo].forEach(foo => bar()); + +> Error 1/1 + + `␊ + 1 | const foo = 1;␊ + > 2 | a[foo].forEach(foo => bar());␊ + | ^^^^^^^ Do not use `Array#forEach(…)`.␊ + ` + +## Invalid #27 + 1 | const foo = 1; + 2 | function unicorn() { + 3 | a[foo].forEach(foo => bar()); + 4 | } + +> Error 1/1 + + `␊ + 1 | const foo = 1;␊ + 2 | function unicorn() {␊ + > 3 | a[foo].forEach(foo => bar());␊ + | ^^^^^^^ Do not use `Array#forEach(…)`.␊ + 4 | }␊ + ` + +## Invalid #28 + 1 | a[index].forEach((b, index) => bar()); + +> Error 1/1 + + `␊ + > 1 | a[index].forEach((b, index) => bar());␊ + | ^^^^^^^ Do not use `Array#forEach(…)`.␊ + ` + +## Invalid #29 + 1 | a((foo) => foo).forEach(foo => bar()); + +> Output + + `␊ + 1 | for (const foo of a((foo) => foo)) bar();␊ + ` + +> Error 1/1 + + `␊ + > 1 | a((foo) => foo).forEach(foo => bar());␊ + | ^^^^^^^ Do not use `Array#forEach(…)`.␊ + ` + +## Invalid #30 + 1 | a((foo, index) => foo + index).forEach((foo, index) => bar()); + +> Output + + `␊ + 1 | for (const [foo, index] of a((foo, index) => foo + index).entries()) bar();␊ + ` + +> Error 1/1 + + `␊ + > 1 | a((foo, index) => foo + index).forEach((foo, index) => bar());␊ + | ^^^^^^^ Do not use `Array#forEach(…)`.␊ + ` + +## Invalid #31 + 1 | const foo = []; + 2 | const index = 1; + 3 | a.forEach((foo, index) => foo[index]); + +> Output + + `␊ + 1 | const foo = [];␊ + 2 | const index = 1;␊ + 3 | for (const [foo, index] of a.entries()) foo[index];␊ + ` + +> Error 1/1 + + `␊ + 1 | const foo = [];␊ + 2 | const index = 1;␊ + > 3 | a.forEach((foo, index) => foo[index]);␊ + | ^^^^^^^ Do not use `Array#forEach(…)`.␊ + ` + +## Invalid #32 + 1 | foo.forEach(function a(element) { + 2 | bar(a) + 3 | }) + +> Error 1/1 + + `␊ + > 1 | foo.forEach(function a(element) {␊ + | ^^^^^^^ Do not use `Array#forEach(…)`.␊ + 2 | bar(a)␊ + 3 | })␊ + ` + +## Invalid #33 + 1 | foo.forEach(function a(element) { + 2 | function b() { + 3 | bar(a) + 4 | } + 5 | }) + +> Error 1/1 + + `␊ + > 1 | foo.forEach(function a(element) {␊ + | ^^^^^^^ Do not use `Array#forEach(…)`.␊ + 2 | function b() {␊ + 3 | bar(a)␊ + 4 | }␊ + 5 | })␊ + ` + +## Invalid #34 + 1 | foo.forEach(function a(element) { + 2 | function b(a) { + 3 | bar(a) + 4 | } + 5 | }) + +> Output + + `␊ + 1 | for (const element of foo) {␊ + 2 | function b(a) {␊ + 3 | bar(a)␊ + 4 | }␊ + 5 | }␊ + ` + +> Error 1/1 + + `␊ + > 1 | foo.forEach(function a(element) {␊ + | ^^^^^^^ Do not use `Array#forEach(…)`.␊ + 2 | function b(a) {␊ + 3 | bar(a)␊ + 4 | }␊ + 5 | })␊ + ` + +## Invalid #35 + 1 | foo.forEach(function(element) { + 2 | bar(this) + 3 | }) + +> Error 1/1 + + `␊ + > 1 | foo.forEach(function(element) {␊ + | ^^^^^^^ Do not use `Array#forEach(…)`.␊ + 2 | bar(this)␊ + 3 | })␊ + ` + +## Invalid #36 + 1 | foo.forEach(function(element) { + 2 | function b() { + 3 | bar(this) + 4 | } + 5 | }) + +> Output + + `␊ + 1 | for (const element of foo) {␊ + 2 | function b() {␊ + 3 | bar(this)␊ + 4 | }␊ + 5 | }␊ + ` + +> Error 1/1 + + `␊ + > 1 | foo.forEach(function(element) {␊ + | ^^^^^^^ Do not use `Array#forEach(…)`.␊ + 2 | function b() {␊ + 3 | bar(this)␊ + 4 | }␊ + 5 | })␊ + ` + +## Invalid #37 + 1 | foo.forEach(function(element) { + 2 | const x = b => { + 3 | bar(this) + 4 | } + 5 | }) + +> Output + + `␊ + 1 | for (const element of foo) {␊ + 2 | const x = b => {␊ + 3 | bar(this)␊ + 4 | }␊ + 5 | }␊ + ` + +> Error 1/1 + + `␊ + > 1 | foo.forEach(function(element) {␊ + | ^^^^^^^ Do not use `Array#forEach(…)`.␊ + 2 | const x = b => {␊ + 3 | bar(this)␊ + 4 | }␊ + 5 | })␊ + ` + +## Invalid #38 + 1 | foo.forEach((element) => { + 2 | bar(this) + 3 | }) + +> Output + + `␊ + 1 | for (const element of foo) {␊ + 2 | bar(this)␊ + 3 | }␊ + ` + +> Error 1/1 + + `␊ + > 1 | foo.forEach((element) => {␊ + | ^^^^^^^ Do not use `Array#forEach(…)`.␊ + 2 | bar(this)␊ + 3 | })␊ + ` + +## Invalid #39 + 1 | foo.forEach(function(element) { + 2 | bar(arguments) + 3 | }) + +> Error 1/1 + + `␊ + > 1 | foo.forEach(function(element) {␊ + | ^^^^^^^ Do not use `Array#forEach(…)`.␊ + 2 | bar(arguments)␊ + 3 | })␊ + ` + +## Invalid #40 + 1 | foo.forEach(function(element) { + 2 | function b() { + 3 | bar(arguments) + 4 | } + 5 | }) + +> Output + + `␊ + 1 | for (const element of foo) {␊ + 2 | function b() {␊ + 3 | bar(arguments)␊ + 4 | }␊ + 5 | }␊ + ` + +> Error 1/1 + + `␊ + > 1 | foo.forEach(function(element) {␊ + | ^^^^^^^ Do not use `Array#forEach(…)`.␊ + 2 | function b() {␊ + 3 | bar(arguments)␊ + 4 | }␊ + 5 | })␊ + ` + +## Invalid #41 + 1 | foo.forEach(function(element) { + 2 | const b = () => { + 3 | bar(arguments) + 4 | } + 5 | }) + +> Output + + `␊ + 1 | for (const element of foo) {␊ + 2 | const b = () => {␊ + 3 | bar(arguments)␊ + 4 | }␊ + 5 | }␊ + ` + +> Error 1/1 + + `␊ + > 1 | foo.forEach(function(element) {␊ + | ^^^^^^^ Do not use `Array#forEach(…)`.␊ + 2 | const b = () => {␊ + 3 | bar(arguments)␊ + 4 | }␊ + 5 | })␊ + ` + +## Invalid #42 + 1 | foo.forEach((element) => { + 2 | bar(arguments) + 3 | }) + +> Output + + `␊ + 1 | for (const element of foo) {␊ + 2 | bar(arguments)␊ + 3 | }␊ + ` + +> Error 1/1 + + `␊ + > 1 | foo.forEach((element) => {␊ + | ^^^^^^^ Do not use `Array#forEach(…)`.␊ + 2 | bar(arguments)␊ + 3 | })␊ + ` + +## Invalid #43 + 1 | foo.forEach(function (element) { + 2 | bar(element); + 3 | }); + +> Output + + `␊ + 1 | for (const element of foo) {␊ + 2 | bar(element);␊ + 3 | }␊ + ` + +> Error 1/1 + + `␊ + > 1 | foo.forEach(function (element) {␊ + | ^^^^^^^ Do not use `Array#forEach(…)`.␊ + 2 | bar(element);␊ + 3 | });␊ + ` + +## Invalid #44 + 1 | foo.forEach(function withName(element) { + 2 | bar(element); + 3 | }); + +> Output + + `␊ + 1 | for (const element of foo) {␊ + 2 | bar(element);␊ + 3 | }␊ + ` + +> Error 1/1 + + `␊ + > 1 | foo.forEach(function withName(element) {␊ + | ^^^^^^^ Do not use `Array#forEach(…)`.␊ + 2 | bar(element);␊ + 3 | });␊ + ` + +## Invalid #45 + 1 | foo.forEach((element) => { + 2 | bar(element); + 3 | }); + +> Output + + `␊ + 1 | for (const element of foo) {␊ + 2 | bar(element);␊ + 3 | }␊ + ` + +> Error 1/1 + + `␊ + > 1 | foo.forEach((element) => {␊ + | ^^^^^^^ Do not use `Array#forEach(…)`.␊ + 2 | bar(element);␊ + 3 | });␊ + ` + +## Invalid #46 + 1 | foo.forEach((element) => bar(element)); + +> Output + + `␊ + 1 | for (const element of foo) bar(element);␊ + ` + +> Error 1/1 + + `␊ + > 1 | foo.forEach((element) => bar(element));␊ + | ^^^^^^^ Do not use `Array#forEach(…)`.␊ + ` + +## Invalid #47 + 1 | foo.forEach(function (element, index) { + 2 | bar(element, index); + 3 | }); + +> Output + + `␊ + 1 | for (const [element, index] of foo.entries()) {␊ + 2 | bar(element, index);␊ + 3 | }␊ + ` + +> Error 1/1 + + `␊ + > 1 | foo.forEach(function (element, index) {␊ + | ^^^^^^^ Do not use `Array#forEach(…)`.␊ + 2 | bar(element, index);␊ + 3 | });␊ + ` + +## Invalid #48 + 1 | foo.forEach(function withName(element, index) { + 2 | bar(element, index); + 3 | }); + +> Output + + `␊ + 1 | for (const [element, index] of foo.entries()) {␊ + 2 | bar(element, index);␊ + 3 | }␊ + ` + +> Error 1/1 + + `␊ + > 1 | foo.forEach(function withName(element, index) {␊ + | ^^^^^^^ Do not use `Array#forEach(…)`.␊ + 2 | bar(element, index);␊ + 3 | });␊ + ` + +## Invalid #49 + 1 | foo.forEach((element, index) => { + 2 | bar(element, index); + 3 | }); + +> Output + + `␊ + 1 | for (const [element, index] of foo.entries()) {␊ + 2 | bar(element, index);␊ + 3 | }␊ + ` + +> Error 1/1 + + `␊ + > 1 | foo.forEach((element, index) => {␊ + | ^^^^^^^ Do not use `Array#forEach(…)`.␊ + 2 | bar(element, index);␊ + 3 | });␊ + ` + +## Invalid #50 + 1 | foo.forEach((element, index) => bar(element, index)); + +> Output + + `␊ + 1 | for (const [element, index] of foo.entries()) bar(element, index);␊ + ` + +> Error 1/1 + + `␊ + > 1 | foo.forEach((element, index) => bar(element, index));␊ + | ^^^^^^^ Do not use `Array#forEach(…)`.␊ + ` + +## Invalid #51 + 1 | (foo).forEach((element, index) => bar(element, index)) + +> Output + + `␊ + 1 | for (const [element, index] of (foo).entries()) bar(element, index)␊ + ` + +> Error 1/1 + + `␊ + > 1 | (foo).forEach((element, index) => bar(element, index))␊ + | ^^^^^^^ Do not use `Array#forEach(…)`.␊ + ` + +## Invalid #52 + 1 | (0, foo).forEach((element, index) => bar(element, index)) + +> Output + + `␊ + 1 | for (const [element, index] of (0, foo).entries()) bar(element, index)␊ + ` + +> Error 1/1 + + `␊ + > 1 | (0, foo).forEach((element, index) => bar(element, index))␊ + | ^^^^^^^ Do not use `Array#forEach(…)`.␊ + ` + +## Invalid #53 + 1 | foo.forEach(function (element) { + 2 | bar(element); + 3 | },); + +> Output + + `␊ + 1 | for (const element of foo) {␊ + 2 | bar(element);␊ + 3 | }␊ + ` + +> Error 1/1 + + `␊ + > 1 | foo.forEach(function (element) {␊ + | ^^^^^^^ Do not use `Array#forEach(…)`.␊ + 2 | bar(element);␊ + 3 | },);␊ + ` + +## Invalid #54 + 1 | foo.forEach(function withName(element) { + 2 | bar(element); + 3 | },); + +> Output + + `␊ + 1 | for (const element of foo) {␊ + 2 | bar(element);␊ + 3 | }␊ + ` + +> Error 1/1 + + `␊ + > 1 | foo.forEach(function withName(element) {␊ + | ^^^^^^^ Do not use `Array#forEach(…)`.␊ + 2 | bar(element);␊ + 3 | },);␊ + ` + +## Invalid #55 + 1 | foo.forEach((element) => { + 2 | bar(element); + 3 | },); + +> Output + + `␊ + 1 | for (const element of foo) {␊ + 2 | bar(element);␊ + 3 | }␊ + ` + +> Error 1/1 + + `␊ + > 1 | foo.forEach((element) => {␊ + | ^^^^^^^ Do not use `Array#forEach(…)`.␊ + 2 | bar(element);␊ + 3 | },);␊ + ` + +## Invalid #56 + 1 | foo.forEach((element) => bar(element),); + +> Output + + `␊ + 1 | for (const element of foo) bar(element);␊ + ` + +> Error 1/1 + + `␊ + > 1 | foo.forEach((element) => bar(element),);␊ + | ^^^^^^^ Do not use `Array#forEach(…)`.␊ + ` + +## Invalid #57 + 1 | foo.forEach((element) => bar(element)) + 2 | ;[foo].pop(); + +> Output + + `␊ + 1 | for (const element of foo) bar(element)␊ + 2 | ;[foo].pop();␊ + ` + +> Error 1/1 + + `␊ + > 1 | foo.forEach((element) => bar(element))␊ + | ^^^^^^^ Do not use `Array#forEach(…)`.␊ + 2 | ;[foo].pop();␊ + ` + +## Invalid #58 + 1 | foo.forEach((element) => { + 2 | bar(element); + 3 | }); + 4 | function noneRelatedFunction() { + 5 | while (element) { + 6 | return; + 7 | } + 8 | } + +> Output + + `␊ + 1 | for (const element of foo) {␊ + 2 | bar(element);␊ + 3 | }␊ + 4 | function noneRelatedFunction() {␊ + 5 | while (element) {␊ + 6 | return;␊ + 7 | }␊ + 8 | }␊ + ` + +> Error 1/1 + + `␊ + > 1 | foo.forEach((element) => {␊ + | ^^^^^^^ Do not use `Array#forEach(…)`.␊ + 2 | bar(element);␊ + 3 | });␊ + 4 | function noneRelatedFunction() {␊ + 5 | while (element) {␊ + 6 | return;␊ + 7 | }␊ + 8 | }␊ + ` + +## Invalid #59 + 1 | foo.forEach(element => ({})) + +> Output + + `␊ + 1 | for (const element of foo) ({})␊ + ` + +> Error 1/1 + + `␊ + > 1 | foo.forEach(element => ({}))␊ + | ^^^^^^^ Do not use `Array#forEach(…)`.␊ + ` + +## Invalid #60 + 1 | foo.forEach((((((element => bar(element))))))); + +> Output + + `␊ + 1 | for (const element of foo) bar(element);␊ + ` + +> Error 1/1 + + `␊ + > 1 | foo.forEach((((((element => bar(element)))))));␊ + | ^^^^^^^ Do not use `Array#forEach(…)`.␊ + ` + +## Invalid #61 + 1 | foo.forEach((element) => { + 2 | if (1) { + 3 | return; + 4 | } + 5 | if (1) { + 6 | return + 7 | } + 8 | if (1) { + 9 | return!true; + 10 | } + 11 | if (1) { + 12 | return!true + 13 | } + 14 | if (1) { + 15 | return bar(); + 16 | } + 17 | if (1) { + 18 | return bar() + 19 | unreachable(); + 20 | } + 21 | if (1) { + 22 | return {}; + 23 | } + 24 | if (1) { + 25 | return ({}); + 26 | } + 27 | if (1) { + 28 | return {a} = a; + 29 | } + 30 | if (1) { + 31 | return [a] = a; + 32 | } + 33 | if (1) { + 34 | foo + 35 | return [] + 36 | } + 37 | if (1) { + 38 | foo + 39 | return [foo] = bar; + 40 | } + 41 | }); + +> Output + + `␊ + 1 | for (const element of foo) {␊ + 2 | if (1) {␊ + 3 | continue;␊ + 4 | }␊ + 5 | if (1) {␊ + 6 | continue␊ + 7 | }␊ + 8 | if (1) {␊ + 9 | !true; continue;␊ + 10 | }␊ + 11 | if (1) {␊ + 12 | !true; continue;␊ + 13 | }␊ + 14 | if (1) {␊ + 15 | bar(); continue;␊ + 16 | }␊ + 17 | if (1) {␊ + 18 | bar(); continue;␊ + 19 | unreachable();␊ + 20 | }␊ + 21 | if (1) {␊ + 22 | ({}); continue;␊ + 23 | }␊ + 24 | if (1) {␊ + 25 | ({}); continue;␊ + 26 | }␊ + 27 | if (1) {␊ + 28 | ({a} = a); continue;␊ + 29 | }␊ + 30 | if (1) {␊ + 31 | ([a] = a); continue;␊ + 32 | }␊ + 33 | if (1) {␊ + 34 | foo␊ + 35 | ;[]; continue;␊ + 36 | }␊ + 37 | if (1) {␊ + 38 | foo␊ + 39 | ;([foo] = bar); continue;␊ + 40 | }␊ + 41 | }␊ + ` + +> Error 1/1 + + `␊ + > 1 | foo.forEach((element) => {␊ + | ^^^^^^^ Do not use `Array#forEach(…)`.␊ + 2 | if (1) {␊ + 3 | return;␊ + 4 | }␊ + 5 | if (1) {␊ + 6 | return␊ + 7 | }␊ + 8 | if (1) {␊ + 9 | return!true;␊ + 10 | }␊ + 11 | if (1) {␊ + 12 | return!true␊ + 13 | }␊ + 14 | if (1) {␊ + 15 | return bar();␊ + 16 | }␊ + 17 | if (1) {␊ + 18 | return bar()␊ + 19 | unreachable();␊ + 20 | }␊ + 21 | if (1) {␊ + 22 | return {};␊ + 23 | }␊ + 24 | if (1) {␊ + 25 | return ({});␊ + 26 | }␊ + 27 | if (1) {␊ + 28 | return {a} = a;␊ + 29 | }␊ + 30 | if (1) {␊ + 31 | return [a] = a;␊ + 32 | }␊ + 33 | if (1) {␊ + 34 | foo␊ + 35 | return []␊ + 36 | }␊ + 37 | if (1) {␊ + 38 | foo␊ + 39 | return [foo] = bar;␊ + 40 | }␊ + 41 | });␊ + ` diff --git a/test/snapshots/no-array-for-each.js.snap b/test/snapshots/no-array-for-each.js.snap new file mode 100644 index 0000000000..f7483072a3 Binary files /dev/null and b/test/snapshots/no-array-for-each.js.snap differ