Skip to content

Commit

Permalink
Ignore more types on no-fn-reference-in-iterator and no-reduce rule
Browse files Browse the repository at this point in the history
  • Loading branch information
fisker committed May 29, 2020
1 parent 16f6ef3 commit b9d6272
Show file tree
Hide file tree
Showing 6 changed files with 89 additions and 37 deletions.
14 changes: 6 additions & 8 deletions rules/no-fn-reference-in-iterator.js
Expand Up @@ -2,6 +2,7 @@
const {isParenthesized} = require('eslint-utils');
const getDocumentationUrl = require('./utils/get-documentation-url');
const methodSelector = require('./utils/method-selector');
const {notFunctionSelector} = require('./utils/not-function');

const ERROR_WITH_NAME_MESSAGE_ID = 'error-with-name';
const ERROR_WITHOUT_NAME_MESSAGE_ID = 'error-without-name';
Expand Down Expand Up @@ -127,14 +128,11 @@ function check(context, node, method, options) {
context.report(problem);
}

const ignoredFirstArgumentSelector = `:not(${
[
'[arguments.0.type="FunctionExpression"]',
'[arguments.0.type="ArrowFunctionExpression"]',
'[arguments.0.type="Literal"]',
'[arguments.0.type="Identifier"][arguments.0.name="undefined"]'
].join(',')
})`;
const ignoredFirstArgumentSelector = [
notFunctionSelector('arguments.0'),
'[arguments.0.type!="FunctionExpression"]',
'[arguments.0.type!="ArrowFunctionExpression"]'
].join('');

const create = context => {
const sourceCode = context.getSourceCode();
Expand Down
28 changes: 16 additions & 12 deletions rules/no-reduce.js
@@ -1,20 +1,13 @@
'use strict';
const methodSelector = require('./utils/method-selector');
const getDocumentationUrl = require('./utils/get-documentation-url');
const {notFunctionSelector} = require('./utils/not-function');

const MESSAGE_ID_REDUCE = 'reduce';
const MESSAGE_ID_REDUCE_RIGHT = 'reduceRight';

const ignoredFirstArgumentSelector = `:not(${
[
'[arguments.0.type="Literal"]',
'[arguments.0.type="Identifier"][arguments.0.name="undefined"]'
].join(',')
})`;

const PROTOTYPE_SELECTOR = [
methodSelector({names: ['call', 'apply']}),
ignoredFirstArgumentSelector,
const prototypeSelector = method => [
methodSelector({name: method}),
'[callee.object.type="MemberExpression"]',
'[callee.object.computed=false]',
`:matches(${
Expand Down Expand Up @@ -45,9 +38,16 @@ const PROTOTYPE_SELECTOR = [
})`
].join('');

const PROTOTYPE_CALL_SELECTOR = [
prototypeSelector('call'),
notFunctionSelector('arguments.1')
].join('');

const PROTOTYPE_APPLY_SELECTOR = prototypeSelector('apply');

const METHOD_SELECTOR = [
methodSelector({names: ['reduce', 'reduceRight'], min: 1, max: 2}),
ignoredFirstArgumentSelector
notFunctionSelector('arguments.0')
].join('');

const create = context => {
Expand All @@ -56,9 +56,13 @@ const create = context => {
// For arr.reduce()
context.report({node: node.callee.property, messageId: node.callee.property.name});
},
[PROTOTYPE_SELECTOR](node) {
[PROTOTYPE_CALL_SELECTOR](node) {
// For cases [].reduce.call() and Array.prototype.reduce.call()
context.report({node: node.callee.object.property, messageId: node.callee.object.property.name});
},
[PROTOTYPE_APPLY_SELECTOR](node) {
// For cases [].reduce.apply() and Array.prototype.reduce.apply()
context.report({node: node.callee.object.property, messageId: node.callee.object.property.name});
}
};
};
Expand Down
23 changes: 23 additions & 0 deletions rules/utils/not-function.js
@@ -0,0 +1,23 @@
'use strict';

// AST Types:
// https://github.com/eslint/espree/blob/master/lib/ast-node-types.js#L18
// Only types possible to be `callee` or `argument` are listed
const impossibleNodeTypes = [
'ArrayExpression',
'ClassExpression',
'Literal',
'ObjectExpression',
'TemplateLiteral',
// Technically `this` could be a function, but most likely not
'ThisExpression'
];

const notFunctionSelector = node => [
...impossibleNodeTypes.map(type => `[${node}.type!="${type}"]`),
`:not([${node}.type="Identifier"][${node}.name="undefined"])`
].join('');

module.exports = {
notFunctionSelector
};
15 changes: 7 additions & 8 deletions test/no-fn-reference-in-iterator.js
Expand Up @@ -2,6 +2,7 @@ import test from 'ava';
import avaRuleTester from 'eslint-ava-rule-tester';
import {outdent} from 'outdent';
import rule from '../rules/no-fn-reference-in-iterator';
import notFunctionTypes from './utils/not-function-types';

const ERROR_WITH_NAME_MESSAGE_ID = 'error-with-name';
const ERROR_WITHOUT_NAME_MESSAGE_ID = 'error-without-name';
Expand All @@ -22,8 +23,8 @@ const reduceLikeMethods = [
];

const ruleTester = avaRuleTester(test, {
env: {
es6: true
parserOptions: {
ecmaVersion: 2020
}
});

Expand Down Expand Up @@ -90,15 +91,13 @@ ruleTester.run('no-fn-reference-in-iterator', rule, {
'React.children.forEach(children, fn)',
'Vue.filter(name, fn)',

// First argument is not a function
...notFunctionTypes.map(data => `foo.map(${data})`),

// Ignored
'foo.map(() => {})',
'foo.map(function() {})',
'foo.map(function bar() {})',
'foo.map("string")',
'foo.map(null)',
'foo.map(1)',
'foo.map(true)',
'foo.map(undefined)'
'foo.map(function bar() {})'
],
invalid: [
// Suggestions
Expand Down
16 changes: 7 additions & 9 deletions test/no-reduce.js
Expand Up @@ -3,6 +3,7 @@ import avaRuleTester from 'eslint-ava-rule-tester';
import {flatten} from 'lodash';
import rule from '../rules/no-reduce';
import {outdent} from 'outdent';
import notFunctionTypes from './utils/not-function-types';

const MESSAGE_ID_REDUCE = 'reduce';
const MESSAGE_ID_REDUCE_RIGHT = 'reduceRight';
Expand All @@ -27,14 +28,7 @@ const tests = {
'[1, 2].reduce.call(() => {}, 34)',

// First argument is not a function
'a.reduce(123)',
'a.reduce(\'abc\')',
'a.reduce(null)',
'a.reduce(undefined)',
'a.reduce(123, initialValue)',
'a.reduce(\'abc\', initialValue)',
'a.reduce(null, initialValue)',
'a.reduce(undefined, initialValue)',
...notFunctionTypes.map(data => `foo.reduce(${data})`),

// Test `.reduce`
// Not `CallExpression`
Expand Down Expand Up @@ -104,7 +98,11 @@ const tests = {
'[].reducex.call(arr, foo)',
'[].xreduce.call(arr, foo)',
'Array.prototype.reducex.call(arr, foo)',
'Array.prototype.xreduce.call(arr, foo)'
'Array.prototype.xreduce.call(arr, foo)',

// Second argument is not a function
...notFunctionTypes.map(data => `Array.prototype.reduce.call(foo, ${data})`)

].map(code => [code, code.replace('reduce', 'reduceRight')])),
invalid: flatten([
'arr.reduce((total, item) => total + item)',
Expand Down
30 changes: 30 additions & 0 deletions test/utils/not-function-types.js
@@ -0,0 +1,30 @@
'use strict';

module.exports = [
// ArrayExpression
'[]',
'[element]',
'[...elements]',
// ClassExpression
'class Node {}',
// Literal
'0',
'1',
'0.1',
'""',
'"string"',
'/regex/',
'null',
'0n',
'1n',
'true',
'false',
// ObjectExpression
'{}',
// TemplateLiteral
'`templateLiteral`',
// Undefined
'undefined',
// ThisExpression
'this'
];

0 comments on commit b9d6272

Please sign in to comment.