From 231529aee793251f30416b93dc3d5f9bfa27ee47 Mon Sep 17 00:00:00 2001 From: fisker Cheung Date: Sun, 25 Feb 2024 21:27:56 +0800 Subject: [PATCH] `no-array-callback-reference`: Check logical expressions, check ternaries deeply (#2289) --- rules/no-array-callback-reference.js | 113 ++++-- rules/utils/index.js | 1 + rules/utils/is-node-value-not-function.js | 1 - ...d-parentheses-to-call-expression-callee.js | 22 + test/no-array-callback-reference.mjs | 63 ++- .../no-array-callback-reference.mjs.md | 376 ++++++++++++++++++ .../no-array-callback-reference.mjs.snap | Bin 0 -> 1143 bytes test/utils/not-function-types.mjs | 1 - 8 files changed, 519 insertions(+), 58 deletions(-) create mode 100644 rules/utils/should-add-parentheses-to-call-expression-callee.js create mode 100644 test/snapshots/no-array-callback-reference.mjs.md create mode 100644 test/snapshots/no-array-callback-reference.mjs.snap diff --git a/rules/no-array-callback-reference.js b/rules/no-array-callback-reference.js index ee5ea677cc..e658bbab9a 100644 --- a/rules/no-array-callback-reference.js +++ b/rules/no-array-callback-reference.js @@ -1,7 +1,13 @@ 'use strict'; -const {isParenthesized} = require('@eslint-community/eslint-utils'); const {isMethodCall} = require('./ast/index.js'); -const {isNodeMatches, isNodeValueNotFunction} = require('./utils/index.js'); +const { + isNodeMatches, + isNodeValueNotFunction, + isParenthesized, + getParenthesizedRange, + getParenthesizedText, + shouldAddParenthesesToCallExpressionCallee, +} = require('./utils/index.js'); const ERROR_WITH_NAME_MESSAGE_ID = 'error-with-name'; const ERROR_WITHOUT_NAME_MESSAGE_ID = 'error-without-name'; @@ -25,7 +31,7 @@ const iteratorMethods = new Map([ }, { method: 'filter', - test: node => !(node.callee.object.type === 'Identifier' && node.callee.object.name === 'Vue'), + shouldIgnoreCallExpression: node => (node.callee.object.type === 'Identifier' && node.callee.object.name === 'Vue'), ignore: [ 'Boolean', ], @@ -63,7 +69,7 @@ const iteratorMethods = new Map([ }, { method: 'map', - test: node => !(node.callee.object.type === 'Identifier' && node.callee.object.name === 'types'), + shouldIgnoreCallExpression: node => (node.callee.object.type === 'Identifier' && node.callee.object.name === 'types'), ignore: [ 'String', 'Number', @@ -104,35 +110,39 @@ const iteratorMethods = new Map([ ignore = [], minParameters = 1, returnsUndefined = false, - test, + shouldIgnoreCallExpression, }) => [method, { minParameters, parameters, returnsUndefined, - test(node) { + shouldIgnoreCallExpression(callExpression) { if ( method !== 'reduce' && method !== 'reduceRight' - && isAwaitExpressionArgument(node) + && isAwaitExpressionArgument(callExpression) ) { - return false; + return true; } - if (isNodeMatches(node.callee.object, ignoredCallee)) { - return false; + if (isNodeMatches(callExpression.callee.object, ignoredCallee)) { + return true; } - if (node.callee.object.type === 'CallExpression' && isNodeMatches(node.callee.object.callee, ignoredCallee)) { - return false; + if ( + callExpression.callee.object.type === 'CallExpression' + && isNodeMatches(callExpression.callee.object.callee, ignoredCallee) + ) { + return true; } - const [callback] = node.arguments; - + return shouldIgnoreCallExpression?.(callExpression) ?? false; + }, + shouldIgnoreCallback(callback) { if (callback.type === 'Identifier' && ignore.includes(callback.name)) { - return false; + return true; } - return !test || test(node); + return false; }, }])); @@ -163,9 +173,14 @@ function getProblem(context, node, method, options) { name, method, }, - suggest: [], }; + if (node.type === 'YieldExpression' || node.type === 'AwaitExpression') { + return problem; + } + + problem.suggest = []; + const {parameters, minParameters, returnsUndefined} = options; for (let parameterLength = minParameters; parameterLength <= parameters.length; parameterLength++) { const suggestionParameters = parameters.slice(0, parameterLength).join(', '); @@ -178,16 +193,20 @@ function getProblem(context, node, method, options) { }, fix(fixer) { const {sourceCode} = context; - let nodeText = sourceCode.getText(node); - if (isParenthesized(node, sourceCode) || type === 'ConditionalExpression') { - nodeText = `(${nodeText})`; + let text = getParenthesizedText(node, sourceCode); + + if ( + !isParenthesized(node, sourceCode) + && shouldAddParenthesesToCallExpressionCallee(node) + ) { + text = `(${text})`; } - return fixer.replaceText( - node, + return fixer.replaceTextRange( + getParenthesizedRange(node, sourceCode), returnsUndefined - ? `(${suggestionParameters}) => { ${nodeText}(${suggestionParameters}); }` - : `(${suggestionParameters}) => ${nodeText}(${suggestionParameters})`, + ? `(${suggestionParameters}) => { ${text}(${suggestionParameters}); }` + : `(${suggestionParameters}) => ${text}(${suggestionParameters})`, ); }, }; @@ -198,47 +217,57 @@ function getProblem(context, node, method, options) { return problem; } +function * getTernaryConsequentAndALternate(node) { + if (node.type === 'ConditionalExpression') { + yield * getTernaryConsequentAndALternate(node.consequent); + yield * getTernaryConsequentAndALternate(node.alternate); + return; + } + + yield node; +} + /** @param {import('eslint').Rule.RuleContext} context */ const create = context => ({ - CallExpression(node) { + * CallExpression(callExpression) { if ( - !isMethodCall(node, { + !isMethodCall(callExpression, { minimumArguments: 1, maximumArguments: 2, optionalCall: false, optionalMember: false, computed: false, }) - || node.callee.property.type !== 'Identifier' + || callExpression.callee.property.type !== 'Identifier' ) { return; } - const methodNode = node.callee.property; + const methodNode = callExpression.callee.property; const methodName = methodNode.name; if (!iteratorMethods.has(methodName)) { return; } - const [callback] = node.arguments; - - if ( - callback.type === 'FunctionExpression' - || callback.type === 'ArrowFunctionExpression' - // Ignore all `CallExpression`s include `function.bind()` - || callback.type === 'CallExpression' - || isNodeValueNotFunction(callback) - ) { + const options = iteratorMethods.get(methodName); + if (options.shouldIgnoreCallExpression(callExpression)) { return; } - const options = iteratorMethods.get(methodName); + for (const callback of getTernaryConsequentAndALternate(callExpression.arguments[0])) { + if ( + callback.type === 'FunctionExpression' + || callback.type === 'ArrowFunctionExpression' + // Ignore all `CallExpression`s include `function.bind()` + || callback.type === 'CallExpression' + || options.shouldIgnoreCallback(callback) + || isNodeValueNotFunction(callback) + ) { + continue; + } - if (!options.test(node)) { - return; + yield getProblem(context, callback, methodName, options); } - - return getProblem(context, callback, methodName, options); }, }); diff --git a/rules/utils/index.js b/rules/utils/index.js index 54c4c55f7b..e57698c6c3 100644 --- a/rules/utils/index.js +++ b/rules/utils/index.js @@ -45,6 +45,7 @@ module.exports = { isValueNotUsable: require('./is-value-not-usable.js'), needsSemicolon: require('./needs-semicolon.js'), shouldAddParenthesesToMemberExpressionObject: require('./should-add-parentheses-to-member-expression-object.js'), + shouldAddParenthesesToCallExpressionCallee: require('./should-add-parentheses-to-call-expression-callee.js'), shouldAddParenthesesToAwaitExpressionArgument: require('./should-add-parentheses-to-await-expression-argument.js'), singular: require('./singular.js'), toLocation: require('./to-location.js'), diff --git a/rules/utils/is-node-value-not-function.js b/rules/utils/is-node-value-not-function.js index 36c8359a57..9b144be39a 100644 --- a/rules/utils/is-node-value-not-function.js +++ b/rules/utils/is-node-value-not-function.js @@ -19,7 +19,6 @@ const impossibleNodeTypes = new Set([ const mostLikelyNotNodeTypes = new Set([ 'AssignmentExpression', 'AwaitExpression', - 'LogicalExpression', 'NewExpression', 'TaggedTemplateExpression', 'ThisExpression', diff --git a/rules/utils/should-add-parentheses-to-call-expression-callee.js b/rules/utils/should-add-parentheses-to-call-expression-callee.js new file mode 100644 index 0000000000..734cfc0fe2 --- /dev/null +++ b/rules/utils/should-add-parentheses-to-call-expression-callee.js @@ -0,0 +1,22 @@ +'use strict'; + +/** +Check if parentheses should be added to a `node` when it's used as `callee` of `CallExpression`. + +@param {Node} node - The AST node to check. +@returns {boolean} +*/ +function shouldAddParenthesesToCallExpressionCallee(node) { + return node.type === 'SequenceExpression' + || node.type === 'YieldExpression' + || node.type === 'ArrowFunctionExpression' + || node.type === 'ConditionalExpression' + || node.type === 'AssignmentExpression' + || node.type === 'LogicalExpression' + || node.type === 'BinaryExpression' + || node.type === 'UnaryExpression' + || node.type === 'UpdateExpression' + || node.type === 'NewExpression'; +} + +module.exports = shouldAddParenthesesToCallExpressionCallee; diff --git a/test/no-array-callback-reference.mjs b/test/no-array-callback-reference.mjs index b4aef83710..a9f3bd9e97 100644 --- a/test/no-array-callback-reference.mjs +++ b/test/no-array-callback-reference.mjs @@ -265,25 +265,16 @@ test({ ), // Need parenthesized + invalidTestCase({ - code: 'foo.map(a ? b : c)', + code: 'foo.map(a || b)', method: 'map', suggestions: [ - 'foo.map((element) => (a ? b : c)(element))', - 'foo.map((element, index) => (a ? b : c)(element, index))', - 'foo.map((element, index, array) => (a ? b : c)(element, index, array))', + 'foo.map((element) => (a || b)(element))', + 'foo.map((element, index) => (a || b)(element, index))', + 'foo.map((element, index, array) => (a || b)(element, index, array))', ], }), - // Note: `await` is not handled, not sure if this is needed - // invalidTestCase({ - // code: `foo.map(await foo())`, - // method: 'map', - // suggestions: [ - // `foo.map(async (accumulator, element) => (await foo())(accumulator, element))`, - // `foo.map(async (accumulator, element, index) => (await foo())(accumulator, element, index))`, - // `foo.map(async (accumulator, element, index, array) => (await foo())(accumulator, element, index, array))` - // ] - // }), // Actual messages { @@ -444,3 +435,47 @@ test({ }), ], }); + +// Ternaries +test.snapshot({ + valid: [ + 'foo.map(_ ? () => {} : _ ? () => {} : () => {})', + 'foo.reduce(_ ? () => {} : _ ? () => {} : () => {})', + 'foo.every(_ ? Boolean : _ ? Boolean : Boolean)', + 'foo.map(_ ? String : _ ? Number : Boolean)', + ], + invalid: [ + outdent` + foo.map( + _ + ? String // This one should be ignored + : callback + ); + `, + outdent` + foo.forEach( + _ + ? callbackA + : _ + ? callbackB + : callbackC + ); + `, + // Needs parentheses + // Some of them was ignored since we know they are not callback function + outdent` + async function * foo () { + foo.map((0, bar)); + foo.map(yield bar); + foo.map(yield* bar); + foo.map(() => bar); + foo.map(bar &&= baz); + foo.map(bar || baz); + foo.map(bar + bar); + foo.map(+ bar); + foo.map(++ bar); + foo.map(new Function('')); + } + `, + ], +}); diff --git a/test/snapshots/no-array-callback-reference.mjs.md b/test/snapshots/no-array-callback-reference.mjs.md new file mode 100644 index 0000000000..1dc922a949 --- /dev/null +++ b/test/snapshots/no-array-callback-reference.mjs.md @@ -0,0 +1,376 @@ +# Snapshot report for `test/no-array-callback-reference.mjs` + +The actual snapshot is saved in `no-array-callback-reference.mjs.snap`. + +Generated by [AVA](https://avajs.dev). + +## invalid(1): foo.map( _ ? String // This one should be ignored : callback ); + +> Input + + `␊ + 1 | foo.map(␊ + 2 | _␊ + 3 | ? String // This one should be ignored␊ + 4 | : callback␊ + 5 | );␊ + ` + +> Error 1/1 + + `␊ + 1 | foo.map(␊ + 2 | _␊ + 3 | ? String // This one should be ignored␊ + > 4 | : callback␊ + | ^^^^^^^^ Do not pass function \`callback\` directly to \`.map(…)\`.␊ + 5 | );␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 1/3: Replace function \`callback\` with \`… => callback(element)\`.␊ + 1 | foo.map(␊ + 2 | _␊ + 3 | ? String // This one should be ignored␊ + 4 | : (element) => callback(element)␊ + 5 | );␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 2/3: Replace function \`callback\` with \`… => callback(element, index)\`.␊ + 1 | foo.map(␊ + 2 | _␊ + 3 | ? String // This one should be ignored␊ + 4 | : (element, index) => callback(element, index)␊ + 5 | );␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 3/3: Replace function \`callback\` with \`… => callback(element, index, array)\`.␊ + 1 | foo.map(␊ + 2 | _␊ + 3 | ? String // This one should be ignored␊ + 4 | : (element, index, array) => callback(element, index, array)␊ + 5 | );␊ + ` + +## invalid(2): foo.forEach( _ ? callbackA : _ ? callbackB : callbackC ); + +> Input + + `␊ + 1 | foo.forEach(␊ + 2 | _␊ + 3 | ? callbackA␊ + 4 | : _␊ + 5 | ? callbackB␊ + 6 | : callbackC␊ + 7 | );␊ + ` + +> Error 1/3 + + `␊ + 1 | foo.forEach(␊ + 2 | _␊ + > 3 | ? callbackA␊ + | ^^^^^^^^^ Do not pass function \`callbackA\` directly to \`.forEach(…)\`.␊ + 4 | : _␊ + 5 | ? callbackB␊ + 6 | : callbackC␊ + 7 | );␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 1/3: Replace function \`callbackA\` with \`… => callbackA(element)\`.␊ + 1 | foo.forEach(␊ + 2 | _␊ + 3 | ? (element) => { callbackA(element); }␊ + 4 | : _␊ + 5 | ? callbackB␊ + 6 | : callbackC␊ + 7 | );␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 2/3: Replace function \`callbackA\` with \`… => callbackA(element, index)\`.␊ + 1 | foo.forEach(␊ + 2 | _␊ + 3 | ? (element, index) => { callbackA(element, index); }␊ + 4 | : _␊ + 5 | ? callbackB␊ + 6 | : callbackC␊ + 7 | );␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 3/3: Replace function \`callbackA\` with \`… => callbackA(element, index, array)\`.␊ + 1 | foo.forEach(␊ + 2 | _␊ + 3 | ? (element, index, array) => { callbackA(element, index, array); }␊ + 4 | : _␊ + 5 | ? callbackB␊ + 6 | : callbackC␊ + 7 | );␊ + ` + +> Error 2/3 + + `␊ + 1 | foo.forEach(␊ + 2 | _␊ + 3 | ? callbackA␊ + 4 | : _␊ + > 5 | ? callbackB␊ + | ^^^^^^^^^ Do not pass function \`callbackB\` directly to \`.forEach(…)\`.␊ + 6 | : callbackC␊ + 7 | );␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 1/3: Replace function \`callbackB\` with \`… => callbackB(element)\`.␊ + 1 | foo.forEach(␊ + 2 | _␊ + 3 | ? callbackA␊ + 4 | : _␊ + 5 | ? (element) => { callbackB(element); }␊ + 6 | : callbackC␊ + 7 | );␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 2/3: Replace function \`callbackB\` with \`… => callbackB(element, index)\`.␊ + 1 | foo.forEach(␊ + 2 | _␊ + 3 | ? callbackA␊ + 4 | : _␊ + 5 | ? (element, index) => { callbackB(element, index); }␊ + 6 | : callbackC␊ + 7 | );␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 3/3: Replace function \`callbackB\` with \`… => callbackB(element, index, array)\`.␊ + 1 | foo.forEach(␊ + 2 | _␊ + 3 | ? callbackA␊ + 4 | : _␊ + 5 | ? (element, index, array) => { callbackB(element, index, array); }␊ + 6 | : callbackC␊ + 7 | );␊ + ` + +> Error 3/3 + + `␊ + 1 | foo.forEach(␊ + 2 | _␊ + 3 | ? callbackA␊ + 4 | : _␊ + 5 | ? callbackB␊ + > 6 | : callbackC␊ + | ^^^^^^^^^ Do not pass function \`callbackC\` directly to \`.forEach(…)\`.␊ + 7 | );␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 1/3: Replace function \`callbackC\` with \`… => callbackC(element)\`.␊ + 1 | foo.forEach(␊ + 2 | _␊ + 3 | ? callbackA␊ + 4 | : _␊ + 5 | ? callbackB␊ + 6 | : (element) => { callbackC(element); }␊ + 7 | );␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 2/3: Replace function \`callbackC\` with \`… => callbackC(element, index)\`.␊ + 1 | foo.forEach(␊ + 2 | _␊ + 3 | ? callbackA␊ + 4 | : _␊ + 5 | ? callbackB␊ + 6 | : (element, index) => { callbackC(element, index); }␊ + 7 | );␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 3/3: Replace function \`callbackC\` with \`… => callbackC(element, index, array)\`.␊ + 1 | foo.forEach(␊ + 2 | _␊ + 3 | ? callbackA␊ + 4 | : _␊ + 5 | ? callbackB␊ + 6 | : (element, index, array) => { callbackC(element, index, array); }␊ + 7 | );␊ + ` + +## invalid(3): async function * foo () { foo.map((0, bar)); foo.map(yield bar); foo.map(yield* bar); foo.map(() => bar); foo.map(bar &&= baz); foo.map(bar || baz); foo.map(bar + bar); foo.map(+ bar); foo.map(++ bar); foo.map(new Function('')); } + +> Input + + `␊ + 1 | async function * foo () {␊ + 2 | foo.map((0, bar));␊ + 3 | foo.map(yield bar);␊ + 4 | foo.map(yield* bar);␊ + 5 | foo.map(() => bar);␊ + 6 | foo.map(bar &&= baz);␊ + 7 | foo.map(bar || baz);␊ + 8 | foo.map(bar + bar);␊ + 9 | foo.map(+ bar);␊ + 10 | foo.map(++ bar);␊ + 11 | foo.map(new Function(''));␊ + 12 | }␊ + ` + +> Error 1/4 + + `␊ + 1 | async function * foo () {␊ + > 2 | foo.map((0, bar));␊ + | ^^^^^^ Do not pass function directly to \`.map(…)\`.␊ + 3 | foo.map(yield bar);␊ + 4 | foo.map(yield* bar);␊ + 5 | foo.map(() => bar);␊ + 6 | foo.map(bar &&= baz);␊ + 7 | foo.map(bar || baz);␊ + 8 | foo.map(bar + bar);␊ + 9 | foo.map(+ bar);␊ + 10 | foo.map(++ bar);␊ + 11 | foo.map(new Function(''));␊ + 12 | }␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 1/3: Replace function with \`… => …(element)\`.␊ + 1 | async function * foo () {␊ + 2 | foo.map((element) => (0, bar)(element));␊ + 3 | foo.map(yield bar);␊ + 4 | foo.map(yield* bar);␊ + 5 | foo.map(() => bar);␊ + 6 | foo.map(bar &&= baz);␊ + 7 | foo.map(bar || baz);␊ + 8 | foo.map(bar + bar);␊ + 9 | foo.map(+ bar);␊ + 10 | foo.map(++ bar);␊ + 11 | foo.map(new Function(''));␊ + 12 | }␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 2/3: Replace function with \`… => …(element, index)\`.␊ + 1 | async function * foo () {␊ + 2 | foo.map((element, index) => (0, bar)(element, index));␊ + 3 | foo.map(yield bar);␊ + 4 | foo.map(yield* bar);␊ + 5 | foo.map(() => bar);␊ + 6 | foo.map(bar &&= baz);␊ + 7 | foo.map(bar || baz);␊ + 8 | foo.map(bar + bar);␊ + 9 | foo.map(+ bar);␊ + 10 | foo.map(++ bar);␊ + 11 | foo.map(new Function(''));␊ + 12 | }␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 3/3: Replace function with \`… => …(element, index, array)\`.␊ + 1 | async function * foo () {␊ + 2 | foo.map((element, index, array) => (0, bar)(element, index, array));␊ + 3 | foo.map(yield bar);␊ + 4 | foo.map(yield* bar);␊ + 5 | foo.map(() => bar);␊ + 6 | foo.map(bar &&= baz);␊ + 7 | foo.map(bar || baz);␊ + 8 | foo.map(bar + bar);␊ + 9 | foo.map(+ bar);␊ + 10 | foo.map(++ bar);␊ + 11 | foo.map(new Function(''));␊ + 12 | }␊ + ` + +> Error 2/4 + + `␊ + 1 | async function * foo () {␊ + 2 | foo.map((0, bar));␊ + > 3 | foo.map(yield bar);␊ + | ^^^^^^^^^ Do not pass function directly to \`.map(…)\`.␊ + 4 | foo.map(yield* bar);␊ + 5 | foo.map(() => bar);␊ + 6 | foo.map(bar &&= baz);␊ + 7 | foo.map(bar || baz);␊ + 8 | foo.map(bar + bar);␊ + 9 | foo.map(+ bar);␊ + 10 | foo.map(++ bar);␊ + 11 | foo.map(new Function(''));␊ + 12 | }␊ + ` + +> Error 3/4 + + `␊ + 1 | async function * foo () {␊ + 2 | foo.map((0, bar));␊ + 3 | foo.map(yield bar);␊ + > 4 | foo.map(yield* bar);␊ + | ^^^^^^^^^^ Do not pass function directly to \`.map(…)\`.␊ + 5 | foo.map(() => bar);␊ + 6 | foo.map(bar &&= baz);␊ + 7 | foo.map(bar || baz);␊ + 8 | foo.map(bar + bar);␊ + 9 | foo.map(+ bar);␊ + 10 | foo.map(++ bar);␊ + 11 | foo.map(new Function(''));␊ + 12 | }␊ + ` + +> Error 4/4 + + `␊ + 1 | async function * foo () {␊ + 2 | foo.map((0, bar));␊ + 3 | foo.map(yield bar);␊ + 4 | foo.map(yield* bar);␊ + 5 | foo.map(() => bar);␊ + 6 | foo.map(bar &&= baz);␊ + > 7 | foo.map(bar || baz);␊ + | ^^^^^^^^^^ Do not pass function directly to \`.map(…)\`.␊ + 8 | foo.map(bar + bar);␊ + 9 | foo.map(+ bar);␊ + 10 | foo.map(++ bar);␊ + 11 | foo.map(new Function(''));␊ + 12 | }␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 1/3: Replace function with \`… => …(element)\`.␊ + 1 | async function * foo () {␊ + 2 | foo.map((0, bar));␊ + 3 | foo.map(yield bar);␊ + 4 | foo.map(yield* bar);␊ + 5 | foo.map(() => bar);␊ + 6 | foo.map(bar &&= baz);␊ + 7 | foo.map((element) => (bar || baz)(element));␊ + 8 | foo.map(bar + bar);␊ + 9 | foo.map(+ bar);␊ + 10 | foo.map(++ bar);␊ + 11 | foo.map(new Function(''));␊ + 12 | }␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 2/3: Replace function with \`… => …(element, index)\`.␊ + 1 | async function * foo () {␊ + 2 | foo.map((0, bar));␊ + 3 | foo.map(yield bar);␊ + 4 | foo.map(yield* bar);␊ + 5 | foo.map(() => bar);␊ + 6 | foo.map(bar &&= baz);␊ + 7 | foo.map((element, index) => (bar || baz)(element, index));␊ + 8 | foo.map(bar + bar);␊ + 9 | foo.map(+ bar);␊ + 10 | foo.map(++ bar);␊ + 11 | foo.map(new Function(''));␊ + 12 | }␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 3/3: Replace function with \`… => …(element, index, array)\`.␊ + 1 | async function * foo () {␊ + 2 | foo.map((0, bar));␊ + 3 | foo.map(yield bar);␊ + 4 | foo.map(yield* bar);␊ + 5 | foo.map(() => bar);␊ + 6 | foo.map(bar &&= baz);␊ + 7 | foo.map((element, index, array) => (bar || baz)(element, index, array));␊ + 8 | foo.map(bar + bar);␊ + 9 | foo.map(+ bar);␊ + 10 | foo.map(++ bar);␊ + 11 | foo.map(new Function(''));␊ + 12 | }␊ + ` diff --git a/test/snapshots/no-array-callback-reference.mjs.snap b/test/snapshots/no-array-callback-reference.mjs.snap new file mode 100644 index 0000000000000000000000000000000000000000..10bf00d86430a3037b4bfce33eb5bc518bf1e320 GIT binary patch literal 1143 zcmV--1c>`VRzVTB!$DYjKx!-r2){SDI8S}L0^z>Z-o?4d>qw&5Xc*`})RLM8*x>Z%2Xj=`!M`&tbu z7}QR#~30Nt>mfh-Gl_Vuc*8akB89c8GgCa&6A6Kn&@oq*2UXX)~Swrg&#pTU~qE;NNTyoYuBBF+J4)qOa3#+Y=;T}hXD zQ1u!<9I8BIjlYj)k6(uZ^S?;Jv&7KzLzkXyhMp!R3YmZ>j5{B-`t9x_rl#l-MG0WJYo%h509Jxo z`jcVl=ZVm$6mC?0LW&T%?q?*K$mQe}*Lj_MT@QFS*7<}D%RGb4BhHgn;W*)937{(! zi;LYTn$axfNJresS5QFWs0wmf+=q}fV1xA}qXr_Y56?0o)-Q%wU&4tc;;0f38Fa1D zsa6zHEwRih25h2Y|I89ss{pDMeN;;mo3&Mt3DeVSP(g2f)>eySob%n@^WSgK@d0dh*OQu= zapFGq&g>t%?m zILEu<_Ywge=Y79(&U@?w+UMD`i;SIeWNAmQz|Igynq%IVh{+#}(w`>9N-LpCcM2bn zE3EQ`e6bzntCO#5sPqhO&)a8gUGMiDq)Ro~Ca!VxX2z6hewz}MX{IT;WWwn_!=HEJ z8>K&{^z(ZxLHe1F>T0`yZju5v0~s>f6aHfDeu&GH*624=u)kidHB!RWW{SDsARAWB z|I@7HysZ6Uvi5Cw4Xq+jo1fB4