Skip to content

Commit 231529a

Browse files
authoredFeb 25, 2024
no-array-callback-reference: Check logical expressions, check ternaries deeply (#2289)
1 parent 78810a5 commit 231529a

8 files changed

+519
-58
lines changed
 

‎rules/no-array-callback-reference.js

+71-42
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
11
'use strict';
2-
const {isParenthesized} = require('@eslint-community/eslint-utils');
32
const {isMethodCall} = require('./ast/index.js');
4-
const {isNodeMatches, isNodeValueNotFunction} = require('./utils/index.js');
3+
const {
4+
isNodeMatches,
5+
isNodeValueNotFunction,
6+
isParenthesized,
7+
getParenthesizedRange,
8+
getParenthesizedText,
9+
shouldAddParenthesesToCallExpressionCallee,
10+
} = require('./utils/index.js');
511

612
const ERROR_WITH_NAME_MESSAGE_ID = 'error-with-name';
713
const ERROR_WITHOUT_NAME_MESSAGE_ID = 'error-without-name';
@@ -25,7 +31,7 @@ const iteratorMethods = new Map([
2531
},
2632
{
2733
method: 'filter',
28-
test: node => !(node.callee.object.type === 'Identifier' && node.callee.object.name === 'Vue'),
34+
shouldIgnoreCallExpression: node => (node.callee.object.type === 'Identifier' && node.callee.object.name === 'Vue'),
2935
ignore: [
3036
'Boolean',
3137
],
@@ -63,7 +69,7 @@ const iteratorMethods = new Map([
6369
},
6470
{
6571
method: 'map',
66-
test: node => !(node.callee.object.type === 'Identifier' && node.callee.object.name === 'types'),
72+
shouldIgnoreCallExpression: node => (node.callee.object.type === 'Identifier' && node.callee.object.name === 'types'),
6773
ignore: [
6874
'String',
6975
'Number',
@@ -104,35 +110,39 @@ const iteratorMethods = new Map([
104110
ignore = [],
105111
minParameters = 1,
106112
returnsUndefined = false,
107-
test,
113+
shouldIgnoreCallExpression,
108114
}) => [method, {
109115
minParameters,
110116
parameters,
111117
returnsUndefined,
112-
test(node) {
118+
shouldIgnoreCallExpression(callExpression) {
113119
if (
114120
method !== 'reduce'
115121
&& method !== 'reduceRight'
116-
&& isAwaitExpressionArgument(node)
122+
&& isAwaitExpressionArgument(callExpression)
117123
) {
118-
return false;
124+
return true;
119125
}
120126

121-
if (isNodeMatches(node.callee.object, ignoredCallee)) {
122-
return false;
127+
if (isNodeMatches(callExpression.callee.object, ignoredCallee)) {
128+
return true;
123129
}
124130

125-
if (node.callee.object.type === 'CallExpression' && isNodeMatches(node.callee.object.callee, ignoredCallee)) {
126-
return false;
131+
if (
132+
callExpression.callee.object.type === 'CallExpression'
133+
&& isNodeMatches(callExpression.callee.object.callee, ignoredCallee)
134+
) {
135+
return true;
127136
}
128137

129-
const [callback] = node.arguments;
130-
138+
return shouldIgnoreCallExpression?.(callExpression) ?? false;
139+
},
140+
shouldIgnoreCallback(callback) {
131141
if (callback.type === 'Identifier' && ignore.includes(callback.name)) {
132-
return false;
142+
return true;
133143
}
134144

135-
return !test || test(node);
145+
return false;
136146
},
137147
}]));
138148

@@ -163,9 +173,14 @@ function getProblem(context, node, method, options) {
163173
name,
164174
method,
165175
},
166-
suggest: [],
167176
};
168177

178+
if (node.type === 'YieldExpression' || node.type === 'AwaitExpression') {
179+
return problem;
180+
}
181+
182+
problem.suggest = [];
183+
169184
const {parameters, minParameters, returnsUndefined} = options;
170185
for (let parameterLength = minParameters; parameterLength <= parameters.length; parameterLength++) {
171186
const suggestionParameters = parameters.slice(0, parameterLength).join(', ');
@@ -178,16 +193,20 @@ function getProblem(context, node, method, options) {
178193
},
179194
fix(fixer) {
180195
const {sourceCode} = context;
181-
let nodeText = sourceCode.getText(node);
182-
if (isParenthesized(node, sourceCode) || type === 'ConditionalExpression') {
183-
nodeText = `(${nodeText})`;
196+
let text = getParenthesizedText(node, sourceCode);
197+
198+
if (
199+
!isParenthesized(node, sourceCode)
200+
&& shouldAddParenthesesToCallExpressionCallee(node)
201+
) {
202+
text = `(${text})`;
184203
}
185204

186-
return fixer.replaceText(
187-
node,
205+
return fixer.replaceTextRange(
206+
getParenthesizedRange(node, sourceCode),
188207
returnsUndefined
189-
? `(${suggestionParameters}) => { ${nodeText}(${suggestionParameters}); }`
190-
: `(${suggestionParameters}) => ${nodeText}(${suggestionParameters})`,
208+
? `(${suggestionParameters}) => { ${text}(${suggestionParameters}); }`
209+
: `(${suggestionParameters}) => ${text}(${suggestionParameters})`,
191210
);
192211
},
193212
};
@@ -198,47 +217,57 @@ function getProblem(context, node, method, options) {
198217
return problem;
199218
}
200219

220+
function * getTernaryConsequentAndALternate(node) {
221+
if (node.type === 'ConditionalExpression') {
222+
yield * getTernaryConsequentAndALternate(node.consequent);
223+
yield * getTernaryConsequentAndALternate(node.alternate);
224+
return;
225+
}
226+
227+
yield node;
228+
}
229+
201230
/** @param {import('eslint').Rule.RuleContext} context */
202231
const create = context => ({
203-
CallExpression(node) {
232+
* CallExpression(callExpression) {
204233
if (
205-
!isMethodCall(node, {
234+
!isMethodCall(callExpression, {
206235
minimumArguments: 1,
207236
maximumArguments: 2,
208237
optionalCall: false,
209238
optionalMember: false,
210239
computed: false,
211240
})
212-
|| node.callee.property.type !== 'Identifier'
241+
|| callExpression.callee.property.type !== 'Identifier'
213242
) {
214243
return;
215244
}
216245

217-
const methodNode = node.callee.property;
246+
const methodNode = callExpression.callee.property;
218247
const methodName = methodNode.name;
219248
if (!iteratorMethods.has(methodName)) {
220249
return;
221250
}
222251

223-
const [callback] = node.arguments;
224-
225-
if (
226-
callback.type === 'FunctionExpression'
227-
|| callback.type === 'ArrowFunctionExpression'
228-
// Ignore all `CallExpression`s include `function.bind()`
229-
|| callback.type === 'CallExpression'
230-
|| isNodeValueNotFunction(callback)
231-
) {
252+
const options = iteratorMethods.get(methodName);
253+
if (options.shouldIgnoreCallExpression(callExpression)) {
232254
return;
233255
}
234256

235-
const options = iteratorMethods.get(methodName);
257+
for (const callback of getTernaryConsequentAndALternate(callExpression.arguments[0])) {
258+
if (
259+
callback.type === 'FunctionExpression'
260+
|| callback.type === 'ArrowFunctionExpression'
261+
// Ignore all `CallExpression`s include `function.bind()`
262+
|| callback.type === 'CallExpression'
263+
|| options.shouldIgnoreCallback(callback)
264+
|| isNodeValueNotFunction(callback)
265+
) {
266+
continue;
267+
}
236268

237-
if (!options.test(node)) {
238-
return;
269+
yield getProblem(context, callback, methodName, options);
239270
}
240-
241-
return getProblem(context, callback, methodName, options);
242271
},
243272
});
244273

‎rules/utils/index.js

+1
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ module.exports = {
4545
isValueNotUsable: require('./is-value-not-usable.js'),
4646
needsSemicolon: require('./needs-semicolon.js'),
4747
shouldAddParenthesesToMemberExpressionObject: require('./should-add-parentheses-to-member-expression-object.js'),
48+
shouldAddParenthesesToCallExpressionCallee: require('./should-add-parentheses-to-call-expression-callee.js'),
4849
shouldAddParenthesesToAwaitExpressionArgument: require('./should-add-parentheses-to-await-expression-argument.js'),
4950
singular: require('./singular.js'),
5051
toLocation: require('./to-location.js'),

‎rules/utils/is-node-value-not-function.js

-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ const impossibleNodeTypes = new Set([
1919
const mostLikelyNotNodeTypes = new Set([
2020
'AssignmentExpression',
2121
'AwaitExpression',
22-
'LogicalExpression',
2322
'NewExpression',
2423
'TaggedTemplateExpression',
2524
'ThisExpression',
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
'use strict';
2+
3+
/**
4+
Check if parentheses should be added to a `node` when it's used as `callee` of `CallExpression`.
5+
6+
@param {Node} node - The AST node to check.
7+
@returns {boolean}
8+
*/
9+
function shouldAddParenthesesToCallExpressionCallee(node) {
10+
return node.type === 'SequenceExpression'
11+
|| node.type === 'YieldExpression'
12+
|| node.type === 'ArrowFunctionExpression'
13+
|| node.type === 'ConditionalExpression'
14+
|| node.type === 'AssignmentExpression'
15+
|| node.type === 'LogicalExpression'
16+
|| node.type === 'BinaryExpression'
17+
|| node.type === 'UnaryExpression'
18+
|| node.type === 'UpdateExpression'
19+
|| node.type === 'NewExpression';
20+
}
21+
22+
module.exports = shouldAddParenthesesToCallExpressionCallee;

‎test/no-array-callback-reference.mjs

+49-14
Original file line numberDiff line numberDiff line change
@@ -265,25 +265,16 @@ test({
265265
),
266266

267267
// Need parenthesized
268+
268269
invalidTestCase({
269-
code: 'foo.map(a ? b : c)',
270+
code: 'foo.map(a || b)',
270271
method: 'map',
271272
suggestions: [
272-
'foo.map((element) => (a ? b : c)(element))',
273-
'foo.map((element, index) => (a ? b : c)(element, index))',
274-
'foo.map((element, index, array) => (a ? b : c)(element, index, array))',
273+
'foo.map((element) => (a || b)(element))',
274+
'foo.map((element, index) => (a || b)(element, index))',
275+
'foo.map((element, index, array) => (a || b)(element, index, array))',
275276
],
276277
}),
277-
// Note: `await` is not handled, not sure if this is needed
278-
// invalidTestCase({
279-
// code: `foo.map(await foo())`,
280-
// method: 'map',
281-
// suggestions: [
282-
// `foo.map(async (accumulator, element) => (await foo())(accumulator, element))`,
283-
// `foo.map(async (accumulator, element, index) => (await foo())(accumulator, element, index))`,
284-
// `foo.map(async (accumulator, element, index, array) => (await foo())(accumulator, element, index, array))`
285-
// ]
286-
// }),
287278

288279
// Actual messages
289280
{
@@ -444,3 +435,47 @@ test({
444435
}),
445436
],
446437
});
438+
439+
// Ternaries
440+
test.snapshot({
441+
valid: [
442+
'foo.map(_ ? () => {} : _ ? () => {} : () => {})',
443+
'foo.reduce(_ ? () => {} : _ ? () => {} : () => {})',
444+
'foo.every(_ ? Boolean : _ ? Boolean : Boolean)',
445+
'foo.map(_ ? String : _ ? Number : Boolean)',
446+
],
447+
invalid: [
448+
outdent`
449+
foo.map(
450+
_
451+
? String // This one should be ignored
452+
: callback
453+
);
454+
`,
455+
outdent`
456+
foo.forEach(
457+
_
458+
? callbackA
459+
: _
460+
? callbackB
461+
: callbackC
462+
);
463+
`,
464+
// Needs parentheses
465+
// Some of them was ignored since we know they are not callback function
466+
outdent`
467+
async function * foo () {
468+
foo.map((0, bar));
469+
foo.map(yield bar);
470+
foo.map(yield* bar);
471+
foo.map(() => bar);
472+
foo.map(bar &&= baz);
473+
foo.map(bar || baz);
474+
foo.map(bar + bar);
475+
foo.map(+ bar);
476+
foo.map(++ bar);
477+
foo.map(new Function(''));
478+
}
479+
`,
480+
],
481+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,376 @@
1+
# Snapshot report for `test/no-array-callback-reference.mjs`
2+
3+
The actual snapshot is saved in `no-array-callback-reference.mjs.snap`.
4+
5+
Generated by [AVA](https://avajs.dev).
6+
7+
## invalid(1): foo.map( _ ? String // This one should be ignored : callback );
8+
9+
> Input
10+
11+
`␊
12+
1 | foo.map(␊
13+
2 | _␊
14+
3 | ? String // This one should be ignored␊
15+
4 | : callback␊
16+
5 | );␊
17+
`
18+
19+
> Error 1/1
20+
21+
`␊
22+
1 | foo.map(␊
23+
2 | _␊
24+
3 | ? String // This one should be ignored␊
25+
> 4 | : callback␊
26+
| ^^^^^^^^ Do not pass function \`callback\` directly to \`.map(…)\`.␊
27+
5 | );␊
28+
29+
--------------------------------------------------------------------------------␊
30+
Suggestion 1/3: Replace function \`callback\` with \`… => callback(element)\`.␊
31+
1 | foo.map(␊
32+
2 | _␊
33+
3 | ? String // This one should be ignored␊
34+
4 | : (element) => callback(element)␊
35+
5 | );␊
36+
37+
--------------------------------------------------------------------------------␊
38+
Suggestion 2/3: Replace function \`callback\` with \`… => callback(element, index)\`.␊
39+
1 | foo.map(␊
40+
2 | _␊
41+
3 | ? String // This one should be ignored␊
42+
4 | : (element, index) => callback(element, index)␊
43+
5 | );␊
44+
45+
--------------------------------------------------------------------------------␊
46+
Suggestion 3/3: Replace function \`callback\` with \`… => callback(element, index, array)\`.␊
47+
1 | foo.map(␊
48+
2 | _␊
49+
3 | ? String // This one should be ignored␊
50+
4 | : (element, index, array) => callback(element, index, array)␊
51+
5 | );␊
52+
`
53+
54+
## invalid(2): foo.forEach( _ ? callbackA : _ ? callbackB : callbackC );
55+
56+
> Input
57+
58+
`␊
59+
1 | foo.forEach(␊
60+
2 | _␊
61+
3 | ? callbackA␊
62+
4 | : _␊
63+
5 | ? callbackB␊
64+
6 | : callbackC␊
65+
7 | );␊
66+
`
67+
68+
> Error 1/3
69+
70+
`␊
71+
1 | foo.forEach(␊
72+
2 | _␊
73+
> 3 | ? callbackA␊
74+
| ^^^^^^^^^ Do not pass function \`callbackA\` directly to \`.forEach(…)\`.␊
75+
4 | : _␊
76+
5 | ? callbackB␊
77+
6 | : callbackC␊
78+
7 | );␊
79+
80+
--------------------------------------------------------------------------------␊
81+
Suggestion 1/3: Replace function \`callbackA\` with \`… => callbackA(element)\`.␊
82+
1 | foo.forEach(␊
83+
2 | _␊
84+
3 | ? (element) => { callbackA(element); }␊
85+
4 | : _␊
86+
5 | ? callbackB␊
87+
6 | : callbackC␊
88+
7 | );␊
89+
90+
--------------------------------------------------------------------------------␊
91+
Suggestion 2/3: Replace function \`callbackA\` with \`… => callbackA(element, index)\`.␊
92+
1 | foo.forEach(␊
93+
2 | _␊
94+
3 | ? (element, index) => { callbackA(element, index); }␊
95+
4 | : _␊
96+
5 | ? callbackB␊
97+
6 | : callbackC␊
98+
7 | );␊
99+
100+
--------------------------------------------------------------------------------␊
101+
Suggestion 3/3: Replace function \`callbackA\` with \`… => callbackA(element, index, array)\`.␊
102+
1 | foo.forEach(␊
103+
2 | _␊
104+
3 | ? (element, index, array) => { callbackA(element, index, array); }␊
105+
4 | : _␊
106+
5 | ? callbackB␊
107+
6 | : callbackC␊
108+
7 | );␊
109+
`
110+
111+
> Error 2/3
112+
113+
`␊
114+
1 | foo.forEach(␊
115+
2 | _␊
116+
3 | ? callbackA␊
117+
4 | : _␊
118+
> 5 | ? callbackB␊
119+
| ^^^^^^^^^ Do not pass function \`callbackB\` directly to \`.forEach(…)\`.␊
120+
6 | : callbackC␊
121+
7 | );␊
122+
123+
--------------------------------------------------------------------------------␊
124+
Suggestion 1/3: Replace function \`callbackB\` with \`… => callbackB(element)\`.␊
125+
1 | foo.forEach(␊
126+
2 | _␊
127+
3 | ? callbackA␊
128+
4 | : _␊
129+
5 | ? (element) => { callbackB(element); }␊
130+
6 | : callbackC␊
131+
7 | );␊
132+
133+
--------------------------------------------------------------------------------␊
134+
Suggestion 2/3: Replace function \`callbackB\` with \`… => callbackB(element, index)\`.␊
135+
1 | foo.forEach(␊
136+
2 | _␊
137+
3 | ? callbackA␊
138+
4 | : _␊
139+
5 | ? (element, index) => { callbackB(element, index); }␊
140+
6 | : callbackC␊
141+
7 | );␊
142+
143+
--------------------------------------------------------------------------------␊
144+
Suggestion 3/3: Replace function \`callbackB\` with \`… => callbackB(element, index, array)\`.␊
145+
1 | foo.forEach(␊
146+
2 | _␊
147+
3 | ? callbackA␊
148+
4 | : _␊
149+
5 | ? (element, index, array) => { callbackB(element, index, array); }␊
150+
6 | : callbackC␊
151+
7 | );␊
152+
`
153+
154+
> Error 3/3
155+
156+
`␊
157+
1 | foo.forEach(␊
158+
2 | _␊
159+
3 | ? callbackA␊
160+
4 | : _␊
161+
5 | ? callbackB␊
162+
> 6 | : callbackC␊
163+
| ^^^^^^^^^ Do not pass function \`callbackC\` directly to \`.forEach(…)\`.␊
164+
7 | );␊
165+
166+
--------------------------------------------------------------------------------␊
167+
Suggestion 1/3: Replace function \`callbackC\` with \`… => callbackC(element)\`.␊
168+
1 | foo.forEach(␊
169+
2 | _␊
170+
3 | ? callbackA␊
171+
4 | : _␊
172+
5 | ? callbackB␊
173+
6 | : (element) => { callbackC(element); }␊
174+
7 | );␊
175+
176+
--------------------------------------------------------------------------------␊
177+
Suggestion 2/3: Replace function \`callbackC\` with \`… => callbackC(element, index)\`.␊
178+
1 | foo.forEach(␊
179+
2 | _␊
180+
3 | ? callbackA␊
181+
4 | : _␊
182+
5 | ? callbackB␊
183+
6 | : (element, index) => { callbackC(element, index); }␊
184+
7 | );␊
185+
186+
--------------------------------------------------------------------------------␊
187+
Suggestion 3/3: Replace function \`callbackC\` with \`… => callbackC(element, index, array)\`.␊
188+
1 | foo.forEach(␊
189+
2 | _␊
190+
3 | ? callbackA␊
191+
4 | : _␊
192+
5 | ? callbackB␊
193+
6 | : (element, index, array) => { callbackC(element, index, array); }␊
194+
7 | );␊
195+
`
196+
197+
## 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('')); }
198+
199+
> Input
200+
201+
`␊
202+
1 | async function * foo () {␊
203+
2 | foo.map((0, bar));␊
204+
3 | foo.map(yield bar);␊
205+
4 | foo.map(yield* bar);␊
206+
5 | foo.map(() => bar);␊
207+
6 | foo.map(bar &&= baz);␊
208+
7 | foo.map(bar || baz);␊
209+
8 | foo.map(bar + bar);␊
210+
9 | foo.map(+ bar);␊
211+
10 | foo.map(++ bar);␊
212+
11 | foo.map(new Function(''));␊
213+
12 | }␊
214+
`
215+
216+
> Error 1/4
217+
218+
`␊
219+
1 | async function * foo () {␊
220+
> 2 | foo.map((0, bar));␊
221+
| ^^^^^^ Do not pass function directly to \`.map(…)\`.␊
222+
3 | foo.map(yield bar);␊
223+
4 | foo.map(yield* bar);␊
224+
5 | foo.map(() => bar);␊
225+
6 | foo.map(bar &&= baz);␊
226+
7 | foo.map(bar || baz);␊
227+
8 | foo.map(bar + bar);␊
228+
9 | foo.map(+ bar);␊
229+
10 | foo.map(++ bar);␊
230+
11 | foo.map(new Function(''));␊
231+
12 | }␊
232+
233+
--------------------------------------------------------------------------------␊
234+
Suggestion 1/3: Replace function with \`… => …(element)\`.␊
235+
1 | async function * foo () {␊
236+
2 | foo.map((element) => (0, bar)(element));␊
237+
3 | foo.map(yield bar);␊
238+
4 | foo.map(yield* bar);␊
239+
5 | foo.map(() => bar);␊
240+
6 | foo.map(bar &&= baz);␊
241+
7 | foo.map(bar || baz);␊
242+
8 | foo.map(bar + bar);␊
243+
9 | foo.map(+ bar);␊
244+
10 | foo.map(++ bar);␊
245+
11 | foo.map(new Function(''));␊
246+
12 | }␊
247+
248+
--------------------------------------------------------------------------------␊
249+
Suggestion 2/3: Replace function with \`… => …(element, index)\`.␊
250+
1 | async function * foo () {␊
251+
2 | foo.map((element, index) => (0, bar)(element, index));␊
252+
3 | foo.map(yield bar);␊
253+
4 | foo.map(yield* bar);␊
254+
5 | foo.map(() => bar);␊
255+
6 | foo.map(bar &&= baz);␊
256+
7 | foo.map(bar || baz);␊
257+
8 | foo.map(bar + bar);␊
258+
9 | foo.map(+ bar);␊
259+
10 | foo.map(++ bar);␊
260+
11 | foo.map(new Function(''));␊
261+
12 | }␊
262+
263+
--------------------------------------------------------------------------------␊
264+
Suggestion 3/3: Replace function with \`… => …(element, index, array)\`.␊
265+
1 | async function * foo () {␊
266+
2 | foo.map((element, index, array) => (0, bar)(element, index, array));␊
267+
3 | foo.map(yield bar);␊
268+
4 | foo.map(yield* bar);␊
269+
5 | foo.map(() => bar);␊
270+
6 | foo.map(bar &&= baz);␊
271+
7 | foo.map(bar || baz);␊
272+
8 | foo.map(bar + bar);␊
273+
9 | foo.map(+ bar);␊
274+
10 | foo.map(++ bar);␊
275+
11 | foo.map(new Function(''));␊
276+
12 | }␊
277+
`
278+
279+
> Error 2/4
280+
281+
`␊
282+
1 | async function * foo () {␊
283+
2 | foo.map((0, bar));␊
284+
> 3 | foo.map(yield bar);␊
285+
| ^^^^^^^^^ Do not pass function directly to \`.map(…)\`.␊
286+
4 | foo.map(yield* bar);␊
287+
5 | foo.map(() => bar);␊
288+
6 | foo.map(bar &&= baz);␊
289+
7 | foo.map(bar || baz);␊
290+
8 | foo.map(bar + bar);␊
291+
9 | foo.map(+ bar);␊
292+
10 | foo.map(++ bar);␊
293+
11 | foo.map(new Function(''));␊
294+
12 | }␊
295+
`
296+
297+
> Error 3/4
298+
299+
`␊
300+
1 | async function * foo () {␊
301+
2 | foo.map((0, bar));␊
302+
3 | foo.map(yield bar);␊
303+
> 4 | foo.map(yield* bar);␊
304+
| ^^^^^^^^^^ Do not pass function directly to \`.map(…)\`.␊
305+
5 | foo.map(() => bar);␊
306+
6 | foo.map(bar &&= baz);␊
307+
7 | foo.map(bar || baz);␊
308+
8 | foo.map(bar + bar);␊
309+
9 | foo.map(+ bar);␊
310+
10 | foo.map(++ bar);␊
311+
11 | foo.map(new Function(''));␊
312+
12 | }␊
313+
`
314+
315+
> Error 4/4
316+
317+
`␊
318+
1 | async function * foo () {␊
319+
2 | foo.map((0, bar));␊
320+
3 | foo.map(yield bar);␊
321+
4 | foo.map(yield* bar);␊
322+
5 | foo.map(() => bar);␊
323+
6 | foo.map(bar &&= baz);␊
324+
> 7 | foo.map(bar || baz);␊
325+
| ^^^^^^^^^^ Do not pass function directly to \`.map(…)\`.␊
326+
8 | foo.map(bar + bar);␊
327+
9 | foo.map(+ bar);␊
328+
10 | foo.map(++ bar);␊
329+
11 | foo.map(new Function(''));␊
330+
12 | }␊
331+
332+
--------------------------------------------------------------------------------␊
333+
Suggestion 1/3: Replace function with \`… => …(element)\`.␊
334+
1 | async function * foo () {␊
335+
2 | foo.map((0, bar));␊
336+
3 | foo.map(yield bar);␊
337+
4 | foo.map(yield* bar);␊
338+
5 | foo.map(() => bar);␊
339+
6 | foo.map(bar &&= baz);␊
340+
7 | foo.map((element) => (bar || baz)(element));␊
341+
8 | foo.map(bar + bar);␊
342+
9 | foo.map(+ bar);␊
343+
10 | foo.map(++ bar);␊
344+
11 | foo.map(new Function(''));␊
345+
12 | }␊
346+
347+
--------------------------------------------------------------------------------␊
348+
Suggestion 2/3: Replace function with \`… => …(element, index)\`.␊
349+
1 | async function * foo () {␊
350+
2 | foo.map((0, bar));␊
351+
3 | foo.map(yield bar);␊
352+
4 | foo.map(yield* bar);␊
353+
5 | foo.map(() => bar);␊
354+
6 | foo.map(bar &&= baz);␊
355+
7 | foo.map((element, index) => (bar || baz)(element, index));␊
356+
8 | foo.map(bar + bar);␊
357+
9 | foo.map(+ bar);␊
358+
10 | foo.map(++ bar);␊
359+
11 | foo.map(new Function(''));␊
360+
12 | }␊
361+
362+
--------------------------------------------------------------------------------␊
363+
Suggestion 3/3: Replace function with \`… => …(element, index, array)\`.␊
364+
1 | async function * foo () {␊
365+
2 | foo.map((0, bar));␊
366+
3 | foo.map(yield bar);␊
367+
4 | foo.map(yield* bar);␊
368+
5 | foo.map(() => bar);␊
369+
6 | foo.map(bar &&= baz);␊
370+
7 | foo.map((element, index, array) => (bar || baz)(element, index, array));␊
371+
8 | foo.map(bar + bar);␊
372+
9 | foo.map(+ bar);␊
373+
10 | foo.map(++ bar);␊
374+
11 | foo.map(new Function(''));␊
375+
12 | }␊
376+
`
Binary file not shown.

‎test/utils/not-function-types.mjs

-1
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@ const notFunctionTypes = [
4242
'a = fn', // Could be a function
4343
// 'await fn', // This requires async function to test, ignore for now
4444
'fn()', // Could be a factory returns a function
45-
'fn1 || fn2', // Could be a function
4645
'new ClassReturnsFunction()', // `class` constructor could return a function
4746
'new Function()', // `function`
4847
'fn``', // Same as `CallExpression`

0 commit comments

Comments
 (0)
Please sign in to comment.