From 20dfb650681fa9a7a942e5c65b80500638a9419b Mon Sep 17 00:00:00 2001 From: fisker Cheung Date: Wed, 27 Nov 2019 20:56:28 +0800 Subject: [PATCH] Add `prefer-negative-index` rule (#417) --- docs/rules/prefer-negative-index.md | 23 ++ index.js | 1 + readme.md | 2 + rules/prefer-negative-index.js | 303 +++++++++++++++++++++++ test/prefer-negative-index.js | 365 ++++++++++++++++++++++++++++ 5 files changed, 694 insertions(+) create mode 100644 docs/rules/prefer-negative-index.md create mode 100644 rules/prefer-negative-index.js create mode 100644 test/prefer-negative-index.js diff --git a/docs/rules/prefer-negative-index.md b/docs/rules/prefer-negative-index.md new file mode 100644 index 0000000000..c7dd095f48 --- /dev/null +++ b/docs/rules/prefer-negative-index.md @@ -0,0 +1,23 @@ +# Prefer negative index over `.length - index` for `{String,Array,TypedArray}#slice()` and `Array#splice()` + +Prefer negative index over calculating from `.length` for [`String#slice()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/slice), [`Array#slice`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/slice), [`TypedArray#slice`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/slice) and [`Array#splice()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/splice) + +This rule is fixable. + +## Fail + +```js +foo.slice(foo.length - 2, foo.length - 1); +foo.splice(foo.length - 1, 1); +Array.prototype.slice.call(foo, foo.length - 2, foo.length - 1); +Array.prototype.slice.apply(foo, [foo.length - 2, foo.length - 1]); +``` + +## Pass + +```js +foo.slice(-2, -1); +foo.splice(-1, 1); +Array.prototype.slice.call(foo, -2, -1); +Array.prototype.slice.apply(foo, [-2, -1]); +``` diff --git a/index.js b/index.js index 5c576ff581..6cbb786f59 100644 --- a/index.js +++ b/index.js @@ -49,6 +49,7 @@ module.exports = { 'unicorn/prefer-exponentiation-operator': 'error', 'unicorn/prefer-flat-map': 'error', 'unicorn/prefer-includes': 'error', + 'unicorn/prefer-negative-index': 'error', 'unicorn/prefer-node-append': 'error', 'unicorn/prefer-node-remove': 'error', 'unicorn/prefer-query-selector': 'error', diff --git a/readme.md b/readme.md index f40c6e4753..534eb98321 100644 --- a/readme.md +++ b/readme.md @@ -67,6 +67,7 @@ Configure it in `package.json`. "unicorn/prefer-exponentiation-operator": "error", "unicorn/prefer-flat-map": "error", "unicorn/prefer-includes": "error", + "unicorn/prefer-negative-index": "error", "unicorn/prefer-node-append": "error", "unicorn/prefer-node-remove": "error", "unicorn/prefer-query-selector": "error", @@ -119,6 +120,7 @@ Configure it in `package.json`. - [prefer-exponentiation-operator](docs/rules/prefer-exponentiation-operator.md) - Prefer the exponentiation operator over `Math.pow()` *(fixable)* - [prefer-flat-map](docs/rules/prefer-flat-map.md) - Prefer `.flatMap(…)` over `.map(…).flat()`. *(fixable)* - [prefer-includes](docs/rules/prefer-includes.md) - Prefer `.includes()` over `.indexOf()` when checking for existence or non-existence. *(fixable)* +- [prefer-negative-index](docs/rules/prefer-negative-index.md) - Prefer negative index over `.length - index` for `{String,Array,TypedArray}#slice()` and `Array#splice()`. *(fixable)* - [prefer-node-append](docs/rules/prefer-node-append.md) - Prefer `Node#append()` over `Node#appendChild()`. *(fixable)* - [prefer-node-remove](docs/rules/prefer-node-remove.md) - Prefer `node.remove()` over `parentNode.removeChild(node)` and `parentElement.removeChild(node)`. *(fixable)* - [prefer-query-selector](docs/rules/prefer-query-selector.md) - Prefer `.querySelector()` over `.getElementById()`, `.querySelectorAll()` over `.getElementsByClassName()` and `.getElementsByTagName()`. *(partly fixable)* diff --git a/rules/prefer-negative-index.js b/rules/prefer-negative-index.js new file mode 100644 index 0000000000..454a1e13c4 --- /dev/null +++ b/rules/prefer-negative-index.js @@ -0,0 +1,303 @@ +'use strict'; +const getDocumentationUrl = require('./utils/get-documentation-url'); +const isLiteralValue = require('./utils/is-literal-value'); + +const methods = new Map([ + [ + 'slice', + { + argumentsIndexes: [0, 1], + supportObjects: new Set([ + 'Array', + 'String', + 'ArrayBuffer', + 'Int8Array', + 'Uint8Array', + 'Uint8ClampedArray', + 'Int16Array', + 'Uint16Array', + 'Int32Array', + 'Uint32Array', + 'Float32Array', + 'Float64Array', + 'BigInt64Array', + 'BigUint64Array' + // `{Blob,File}#slice()` are not generally used + // 'Blob' + // 'File' + ]) + } + ], + [ + 'splice', + { + argumentsIndexes: [0], + supportObjects: new Set([ + 'Array' + ]) + } + ] +]); + +const OPERATOR_MINUS = '-'; + +const isPropertiesEqual = (node1, node2) => properties => { + return properties.every(property => isEqual(node1[property], node2[property])); +}; + +const isTemplateElementEqual = (node1, node2) => { + return node1.value && + node2.value && + node1.tail === node2.tail && + isPropertiesEqual(node1.value, node2.value)(['cooked', 'raw']); +}; + +const isTemplateLiteralEqual = (node1, node2) => { + const {quasis: quasis1} = node1; + const {quasis: quasis2} = node2; + + return quasis1.length === quasis2.length && + quasis1.every((templateElement, index) => isEqual(templateElement, quasis2[index])); +}; + +const isEqual = (node1, node2) => { + if (node1 === node2) { + return true; + } + + const compare = isPropertiesEqual(node1, node2); + + if (!compare(['type'])) { + return false; + } + + const {type} = node1; + + switch (type) { + case 'Identifier': + return compare(['name', 'computed']); + case 'Literal': + return compare(['value', 'raw']); + case 'TemplateLiteral': + return isTemplateLiteralEqual(node1, node2); + case 'TemplateElement': + return isTemplateElementEqual(node1, node2); + case 'BinaryExpression': + return compare(['operator', 'left', 'right']); + case 'MemberExpression': + return compare(['object', 'property']); + default: + return false; + } +}; + +const isLengthMemberExpression = node => node && + node.type === 'MemberExpression' && + node.property && + node.property.type === 'Identifier' && + node.property.name === 'length' && + node.object; + +const isLiteralPositiveValue = node => + node && + node.type === 'Literal' && + typeof node.value === 'number' && + node.value > 0; + +const getLengthMemberExpression = node => { + if (!node) { + return; + } + + const {type, operator, left, right} = node; + + if ( + type !== 'BinaryExpression' || + operator !== OPERATOR_MINUS || + !left || + !isLiteralPositiveValue(right) + ) { + return; + } + + if (isLengthMemberExpression(left)) { + return left; + } + + // Nested BinaryExpression + return getLengthMemberExpression(left); +}; + +const getRemoveAbleNode = (target, argument) => { + const lengthMemberExpression = getLengthMemberExpression(argument); + + if (lengthMemberExpression && isEqual(target, lengthMemberExpression.object)) { + return lengthMemberExpression; + } +}; + +const getRemovalRange = (node, sourceCode) => { + let before = sourceCode.getTokenBefore(node); + let after = sourceCode.getTokenAfter(node); + + let [start, end] = node.range; + + let hasParentheses = true; + + while (hasParentheses) { + hasParentheses = + (before.type === 'Punctuator' && before.value === '(') && + (after.type === 'Punctuator' && after.value === ')'); + if (hasParentheses) { + before = sourceCode.getTokenBefore(before); + after = sourceCode.getTokenAfter(after); + start = before.range[1]; + end = after.range[0]; + } + } + + const [nextStart] = after.range; + const textBetween = sourceCode.text.slice(end, nextStart); + + end += textBetween.match(/\S|$/).index; + + return [start, end]; +}; + +const getMemberName = node => { + const {type, property} = node; + + if ( + type === 'MemberExpression' && + property && + property.type === 'Identifier' + ) { + return property.name; + } +}; + +function parse(node) { + const {callee, arguments: originalArguments} = node; + + let method = callee.property.name; + let target = callee.object; + let argumentsNodes = originalArguments; + + if (methods.has(method)) { + return { + method, + target, + argumentsNodes + }; + } + + if (method !== 'call' && method !== 'apply') { + return; + } + + const isApply = method === 'apply'; + + method = getMemberName(callee.object); + + if (!methods.has(method)) { + return; + } + + const { + supportObjects + } = methods.get(method); + + const parentCallee = callee.object.object; + + if ( + // [].{slice,splice} + ( + parentCallee.type === 'ArrayExpression' && + parentCallee.elements.length === 0 + ) || + // ''.slice + ( + method === 'slice' && + isLiteralValue(parentCallee, '') + ) || + // {Array,String...}.prototype.slice + // Array.prototype.splice + ( + getMemberName(parentCallee) === 'prototype' && + parentCallee.object.type === 'Identifier' && + supportObjects.has(parentCallee.object.name) + ) + ) { + [target] = originalArguments; + + if (isApply) { + const [, secondArgument] = originalArguments; + if (secondArgument.type !== 'ArrayExpression') { + return; + } + + argumentsNodes = secondArgument.elements; + } else { + argumentsNodes = originalArguments.slice(1); + } + + return { + method, + target, + argumentsNodes + }; + } +} + +const create = context => ({ + CallExpression: node => { + if (node.callee.type !== 'MemberExpression') { + return; + } + + const parsed = parse(node); + + if (!parsed) { + return; + } + + const { + method, + target, + argumentsNodes + } = parsed; + + const {argumentsIndexes} = methods.get(method); + const removableNodes = argumentsIndexes + .map(index => getRemoveAbleNode(target, argumentsNodes[index])) + .filter(Boolean); + + if (removableNodes.length === 0) { + return; + } + + context.report({ + node, + message: `Prefer negative index over length minus index for \`${method}\`.`, + fix(fixer) { + const sourceCode = context.getSourceCode(); + return removableNodes.map( + node => fixer.removeRange( + getRemovalRange(node, sourceCode) + ) + ); + } + }); + } +}); + +module.exports = { + create, + meta: { + type: 'suggestion', + docs: { + url: getDocumentationUrl(__filename) + }, + fixable: 'code' + } +}; diff --git a/test/prefer-negative-index.js b/test/prefer-negative-index.js new file mode 100644 index 0000000000..144d39ab64 --- /dev/null +++ b/test/prefer-negative-index.js @@ -0,0 +1,365 @@ +import test from 'ava'; +import avaRuleTester from 'eslint-ava-rule-tester'; +import {outdent} from 'outdent'; +import rule from '../rules/prefer-negative-index'; + +const ruleTester = avaRuleTester(test, { + env: { + es6: true + } +}); + +const error = { + ruleId: 'prefer-negative-index' +}; + +ruleTester.run('prefer-negative-index', rule, { + valid: [ + // Docs example (1) + 'foo.slice(-2, -1)', + // Docs example (2) + 'foo.splice(-1, 1)', + // Docs example (3) + 'Array.prototype.slice.call(foo, -2, -1)', + // Docs example (4) + 'Array.prototype.slice.apply(foo, [-2, -1])', + // A function named `slice` + 'slice(foo.length - 1)', + // Not slice + 'foo.forEach(foo.length - 1)', + // Not slice + 'Array.prototype.forEach.call(foo, foo.length - 1)', + // Not Array + 'FOO.prototype.slice.apply(foo, [-2, -1])', + // Second argument is not a array + 'Array.prototype.slice.apply(foo, "")', + // New call + 'new Foo.forEach(Foo.length - 1)', + // Bar.length + 'foo.slice(bar.length - 1)', + // - NOT_POSITIVE_VALUE + 'foo.slice(foo.length - 0)', + // - NOT_NUMBER + 'foo.slice(foo.length - "1")', + // - NOT_LITERAL + 'foo.slice(foo.length - (-1))', + // + BinaryExpression + 'foo.slice(foo.length + 1)', + // Has + BinaryExpression + 'foo.slice(foo.length - 2 + 1)', + // Has + BinaryExpression + 'foo.slice((foo.length - 1) + 1)', + // Has / BinaryExpression + 'foo.slice(foo.length - 1 / 1)', + // ArrayExpression + '[1, 2, 3].slice([1, 2, 3].length - 1)', + // Foo.bar and foo["bar"] + 'foo.bar.slice(foo["bar"].length - 1)', + // Foo[`bar`] and foo["bar"] + 'foo[`bar`].slice(foo["bar"].length - 1)', + // Foo[1] and foo["1"] + 'foo[1].slice(foo["1"].length - 1)', + // Foo[bar++] + 'foo[bar++].slice(foo[bar++].length - 1)', + // Foo['bar'] & foo["bar"] + 'foo[\'bar\'].slice(foo["bar"].length - 1)' + ], + invalid: [ + // Docs example (1) + { + code: 'foo.slice(foo.length - 2, foo.length - 1)', + errors: [error], + output: 'foo.slice(- 2, - 1)' + }, + // Docs example (2) + { + code: 'foo.splice(foo.length - 1, 1)', + errors: [error], + output: 'foo.splice(- 1, 1)' + }, + // Docs example (3) + { + code: 'Array.prototype.slice.call(foo, foo.length - 2, foo.length - 1)', + errors: [error], + output: 'Array.prototype.slice.call(foo, - 2, - 1)' + }, + // Docs example (4) + { + code: 'Array.prototype.slice.apply(foo, [foo.length - 2, foo.length - 1])', + errors: [error], + output: 'Array.prototype.slice.apply(foo, [- 2, - 1])' + }, + // Nested + { + code: 'foo.slice(foo.length - 1 - 1)', + errors: [error], + output: 'foo.slice(- 1 - 1)' + }, + // Foo.bar + { + code: 'foo.bar.slice(foo.bar.length - 1)', + errors: [error], + output: 'foo.bar.slice(- 1)' + }, + // Foo['bar'] + { + code: 'foo[\'bar\'].slice(foo[\'bar\'].length - 1)', + errors: [error], + output: 'foo[\'bar\'].slice(- 1)' + }, + // Foo[1] + { + code: 'foo[1].slice(foo[1].length - 1)', + errors: [error], + output: 'foo[1].slice(- 1)' + }, + // Foo[`${bar}`] + { + // eslint-disable-next-line no-template-curly-in-string + code: 'foo[`${bar}`].slice(foo[`${bar}`].length - 1)', + errors: [error], + // eslint-disable-next-line no-template-curly-in-string + output: 'foo[`${bar}`].slice(- 1)' + }, + // Foo[a + b] + { + code: 'foo[a + b].slice(foo[a + b].length - 1)', + errors: [error], + output: 'foo[a + b].slice(- 1)' + }, + // Comment + { + code: 'foo.slice(foo.length/* comment */ - 1)', + errors: [error], + output: 'foo.slice(/* comment */ - 1)' + }, + // Comment + { + code: outdent` + foo.slice( + // comment 1 + + foo.length + + // comment 2 + - 1 + -1 + , + foo.length // comment 3 + - 1 + ) + `, + errors: [error], + output: outdent` + foo.slice( + // comment 1 + + // comment 2 + - 1 + -1 + , + // comment 3 + - 1 + ) + ` + }, + // Parentheses + { + code: 'foo.slice((((foo.length)) - 1) - 1)', + errors: [error], + output: 'foo.slice((- 1) - 1)' + }, + // Comment inside parentheses + { + code: 'foo.slice(/* will keep */(/* will remove 1 */(/* will remove 2 */(foo.length)) - 1) - 1)', + errors: [error], + output: 'foo.slice(/* will keep */(- 1) - 1)' + }, + // [].{slice,splice} + { + code: outdent` + [].slice.call(foo, foo.length - 1, foo.length - 2, foo.length - 3); + [].splice.call(foo, foo.length - 1, foo.length - 2, foo.length - 3); + [].slice.apply(foo, [foo.length - 1, foo.length - 2, foo.length - 3]); + [].splice.apply(foo, [foo.length - 1, foo.length - 2, foo.length - 3]); + [NOT_EMPTY].slice.call(foo, foo.length - 1, foo.length - 2, foo.length - 3); + [NOT_EMPTY].splice.call(foo, foo.length - 1, foo.length - 2, foo.length - 3); + [NOT_EMPTY].slice.call(foo, [foo.length - 1, foo.length - 2, foo.length - 3]); + [NOT_EMPTY].splice.call(foo, [foo.length - 1, foo.length - 2, foo.length - 3]); + `, + errors: Array.from({length: 4}, () => error), + output: outdent` + [].slice.call(foo, - 1, - 2, foo.length - 3); + [].splice.call(foo, - 1, foo.length - 2, foo.length - 3); + [].slice.apply(foo, [- 1, - 2, foo.length - 3]); + [].splice.apply(foo, [- 1, foo.length - 2, foo.length - 3]); + [NOT_EMPTY].slice.call(foo, foo.length - 1, foo.length - 2, foo.length - 3); + [NOT_EMPTY].splice.call(foo, foo.length - 1, foo.length - 2, foo.length - 3); + [NOT_EMPTY].slice.call(foo, [foo.length - 1, foo.length - 2, foo.length - 3]); + [NOT_EMPTY].splice.call(foo, [foo.length - 1, foo.length - 2, foo.length - 3]); + ` + }, + // ''.slice + { + code: outdent` + ''.slice.call(foo, foo.length - 1, foo.length - 2, foo.length - 3); + ''.splice.call(foo, foo.length - 1, foo.length - 2, foo.length - 3); + ''.slice.apply(foo, [foo.length - 1, foo.length - 2, foo.length - 3]); + ''.splice.apply(foo, [foo.length - 1, foo.length - 2, foo.length - 3]); + 'NOT_EMPTY'.slice.call(foo, foo.length - 1, foo.length - 2, foo.length - 3); + 'NOT_EMPTY'.splice.call(foo, foo.length - 1, foo.length - 2, foo.length - 3); + 'NOT_EMPTY'.slice.apply(foo, [foo.length - 1, foo.length - 2, foo.length - 3]); + 'NOT_EMPTY'.splice.apply(foo, [foo.length - 1, foo.length - 2, foo.length - 3]); + `, + errors: Array.from({length: 2}, () => error), + output: outdent` + ''.slice.call(foo, - 1, - 2, foo.length - 3); + ''.splice.call(foo, foo.length - 1, foo.length - 2, foo.length - 3); + ''.slice.apply(foo, [- 1, - 2, foo.length - 3]); + ''.splice.apply(foo, [foo.length - 1, foo.length - 2, foo.length - 3]); + 'NOT_EMPTY'.slice.call(foo, foo.length - 1, foo.length - 2, foo.length - 3); + 'NOT_EMPTY'.splice.call(foo, foo.length - 1, foo.length - 2, foo.length - 3); + 'NOT_EMPTY'.slice.apply(foo, [foo.length - 1, foo.length - 2, foo.length - 3]); + 'NOT_EMPTY'.splice.apply(foo, [foo.length - 1, foo.length - 2, foo.length - 3]); + ` + }, + // {Array,String...}.prototype.slice.call + // Array.prototype.splice.call + { + code: outdent` + Array.prototype.slice.call(foo, foo.length - 1, foo.length - 2, foo.length - 3); + Array.prototype.splice.call(foo, foo.length - 1, foo.length - 2, foo.length - 3); + String.prototype.slice.call(foo, foo.length - 1, foo.length - 2, foo.length - 3); + String.prototype.splice.call(foo, foo.length - 1, foo.length - 2, foo.length - 3); + ArrayBuffer.prototype.slice.call(foo, foo.length - 1, foo.length - 2, foo.length - 3); + ArrayBuffer.prototype.splice.call(foo, foo.length - 1, foo.length - 2, foo.length - 3); + Int8Array.prototype.slice.call(foo, foo.length - 1, foo.length - 2, foo.length - 3); + Int8Array.prototype.splice.call(foo, foo.length - 1, foo.length - 2, foo.length - 3); + Uint8Array.prototype.slice.call(foo, foo.length - 1, foo.length - 2, foo.length - 3); + Uint8Array.prototype.splice.call(foo, foo.length - 1, foo.length - 2, foo.length - 3); + Uint8ClampedArray.prototype.slice.call(foo, foo.length - 1, foo.length - 2, foo.length - 3); + Uint8ClampedArray.prototype.splice.call(foo, foo.length - 1, foo.length - 2, foo.length - 3); + Int16Array.prototype.slice.call(foo, foo.length - 1, foo.length - 2, foo.length - 3); + Int16Array.prototype.splice.call(foo, foo.length - 1, foo.length - 2, foo.length - 3); + Uint16Array.prototype.slice.call(foo, foo.length - 1, foo.length - 2, foo.length - 3); + Uint16Array.prototype.splice.call(foo, foo.length - 1, foo.length - 2, foo.length - 3); + Int32Array.prototype.slice.call(foo, foo.length - 1, foo.length - 2, foo.length - 3); + Int32Array.prototype.splice.call(foo, foo.length - 1, foo.length - 2, foo.length - 3); + Uint32Array.prototype.slice.call(foo, foo.length - 1, foo.length - 2, foo.length - 3); + Uint32Array.prototype.splice.call(foo, foo.length - 1, foo.length - 2, foo.length - 3); + Float32Array.prototype.slice.call(foo, foo.length - 1, foo.length - 2, foo.length - 3); + Float32Array.prototype.splice.call(foo, foo.length - 1, foo.length - 2, foo.length - 3); + Float64Array.prototype.slice.call(foo, foo.length - 1, foo.length - 2, foo.length - 3); + Float64Array.prototype.splice.call(foo, foo.length - 1, foo.length - 2, foo.length - 3); + BigInt64Array.prototype.slice.call(foo, foo.length - 1, foo.length - 2, foo.length - 3); + BigInt64Array.prototype.splice.call(foo, foo.length - 1, foo.length - 2, foo.length - 3); + BigUint64Array.prototype.slice.call(foo, foo.length - 1, foo.length - 2, foo.length - 3); + BigUint64Array.prototype.splice.call(foo, foo.length - 1, foo.length - 2, foo.length - 3); + NOT_SUPPORTED.prototype.slice.call(foo, foo.length - 1, foo.length - 2, foo.length - 3); + NOT_SUPPORTED.prototype.splice.call(foo, foo.length - 1, foo.length - 2, foo.length - 3); + `, + errors: Array.from({length: 15}, () => error), + output: outdent` + Array.prototype.slice.call(foo, - 1, - 2, foo.length - 3); + Array.prototype.splice.call(foo, - 1, foo.length - 2, foo.length - 3); + String.prototype.slice.call(foo, - 1, - 2, foo.length - 3); + String.prototype.splice.call(foo, foo.length - 1, foo.length - 2, foo.length - 3); + ArrayBuffer.prototype.slice.call(foo, - 1, - 2, foo.length - 3); + ArrayBuffer.prototype.splice.call(foo, foo.length - 1, foo.length - 2, foo.length - 3); + Int8Array.prototype.slice.call(foo, - 1, - 2, foo.length - 3); + Int8Array.prototype.splice.call(foo, foo.length - 1, foo.length - 2, foo.length - 3); + Uint8Array.prototype.slice.call(foo, - 1, - 2, foo.length - 3); + Uint8Array.prototype.splice.call(foo, foo.length - 1, foo.length - 2, foo.length - 3); + Uint8ClampedArray.prototype.slice.call(foo, - 1, - 2, foo.length - 3); + Uint8ClampedArray.prototype.splice.call(foo, foo.length - 1, foo.length - 2, foo.length - 3); + Int16Array.prototype.slice.call(foo, - 1, - 2, foo.length - 3); + Int16Array.prototype.splice.call(foo, foo.length - 1, foo.length - 2, foo.length - 3); + Uint16Array.prototype.slice.call(foo, - 1, - 2, foo.length - 3); + Uint16Array.prototype.splice.call(foo, foo.length - 1, foo.length - 2, foo.length - 3); + Int32Array.prototype.slice.call(foo, - 1, - 2, foo.length - 3); + Int32Array.prototype.splice.call(foo, foo.length - 1, foo.length - 2, foo.length - 3); + Uint32Array.prototype.slice.call(foo, - 1, - 2, foo.length - 3); + Uint32Array.prototype.splice.call(foo, foo.length - 1, foo.length - 2, foo.length - 3); + Float32Array.prototype.slice.call(foo, - 1, - 2, foo.length - 3); + Float32Array.prototype.splice.call(foo, foo.length - 1, foo.length - 2, foo.length - 3); + Float64Array.prototype.slice.call(foo, - 1, - 2, foo.length - 3); + Float64Array.prototype.splice.call(foo, foo.length - 1, foo.length - 2, foo.length - 3); + BigInt64Array.prototype.slice.call(foo, - 1, - 2, foo.length - 3); + BigInt64Array.prototype.splice.call(foo, foo.length - 1, foo.length - 2, foo.length - 3); + BigUint64Array.prototype.slice.call(foo, - 1, - 2, foo.length - 3); + BigUint64Array.prototype.splice.call(foo, foo.length - 1, foo.length - 2, foo.length - 3); + NOT_SUPPORTED.prototype.slice.call(foo, foo.length - 1, foo.length - 2, foo.length - 3); + NOT_SUPPORTED.prototype.splice.call(foo, foo.length - 1, foo.length - 2, foo.length - 3); + ` + }, + // {Array,String...}.prototype.slice.apply + // Array.prototype.splice.apply + { + code: outdent` + Array.prototype.slice.apply(foo, [foo.length - 1, foo.length - 2, foo.length - 3]); + Array.prototype.splice.apply(foo, [foo.length - 1, foo.length - 2, foo.length - 3]); + String.prototype.slice.apply(foo, [foo.length - 1, foo.length - 2, foo.length - 3]); + String.prototype.splice.apply(foo, [foo.length - 1, foo.length - 2, foo.length - 3]); + ArrayBuffer.prototype.slice.apply(foo, [foo.length - 1, foo.length - 2, foo.length - 3]); + ArrayBuffer.prototype.splice.apply(foo, [foo.length - 1, foo.length - 2, foo.length - 3]); + Int8Array.prototype.slice.apply(foo, [foo.length - 1, foo.length - 2, foo.length - 3]); + Int8Array.prototype.splice.apply(foo, [foo.length - 1, foo.length - 2, foo.length - 3]); + Uint8Array.prototype.slice.apply(foo, [foo.length - 1, foo.length - 2, foo.length - 3]); + Uint8Array.prototype.splice.apply(foo, [foo.length - 1, foo.length - 2, foo.length - 3]); + Uint8ClampedArray.prototype.slice.apply(foo, [foo.length - 1, foo.length - 2, foo.length - 3]); + Uint8ClampedArray.prototype.splice.apply(foo, [foo.length - 1, foo.length - 2, foo.length - 3]); + Int16Array.prototype.slice.apply(foo, [foo.length - 1, foo.length - 2, foo.length - 3]); + Int16Array.prototype.splice.apply(foo, [foo.length - 1, foo.length - 2, foo.length - 3]); + Uint16Array.prototype.slice.apply(foo, [foo.length - 1, foo.length - 2, foo.length - 3]); + Uint16Array.prototype.splice.apply(foo, [foo.length - 1, foo.length - 2, foo.length - 3]); + Int32Array.prototype.slice.apply(foo, [foo.length - 1, foo.length - 2, foo.length - 3]); + Int32Array.prototype.splice.apply(foo, [foo.length - 1, foo.length - 2, foo.length - 3]); + Uint32Array.prototype.slice.apply(foo, [foo.length - 1, foo.length - 2, foo.length - 3]); + Uint32Array.prototype.splice.apply(foo, [foo.length - 1, foo.length - 2, foo.length - 3]); + Float32Array.prototype.slice.apply(foo, [foo.length - 1, foo.length - 2, foo.length - 3]); + Float32Array.prototype.splice.apply(foo, [foo.length - 1, foo.length - 2, foo.length - 3]); + Float64Array.prototype.slice.apply(foo, [foo.length - 1, foo.length - 2, foo.length - 3]); + Float64Array.prototype.splice.apply(foo, [foo.length - 1, foo.length - 2, foo.length - 3]); + BigInt64Array.prototype.slice.apply(foo, [foo.length - 1, foo.length - 2, foo.length - 3]); + BigInt64Array.prototype.splice.apply(foo, [foo.length - 1, foo.length - 2, foo.length - 3]); + BigUint64Array.prototype.slice.apply(foo, [foo.length - 1, foo.length - 2, foo.length - 3]); + BigUint64Array.prototype.splice.apply(foo, [foo.length - 1, foo.length - 2, foo.length - 3]); + NOT_SUPPORTED.prototype.slice.apply(foo, [foo.length - 1, foo.length - 2, foo.length - 3]); + NOT_SUPPORTED.prototype.splice.apply(foo, [foo.length - 1, foo.length - 2, foo.length - 3]); + `, + errors: Array.from({length: 15}, () => error), + output: outdent` + Array.prototype.slice.apply(foo, [- 1, - 2, foo.length - 3]); + Array.prototype.splice.apply(foo, [- 1, foo.length - 2, foo.length - 3]); + String.prototype.slice.apply(foo, [- 1, - 2, foo.length - 3]); + String.prototype.splice.apply(foo, [foo.length - 1, foo.length - 2, foo.length - 3]); + ArrayBuffer.prototype.slice.apply(foo, [- 1, - 2, foo.length - 3]); + ArrayBuffer.prototype.splice.apply(foo, [foo.length - 1, foo.length - 2, foo.length - 3]); + Int8Array.prototype.slice.apply(foo, [- 1, - 2, foo.length - 3]); + Int8Array.prototype.splice.apply(foo, [foo.length - 1, foo.length - 2, foo.length - 3]); + Uint8Array.prototype.slice.apply(foo, [- 1, - 2, foo.length - 3]); + Uint8Array.prototype.splice.apply(foo, [foo.length - 1, foo.length - 2, foo.length - 3]); + Uint8ClampedArray.prototype.slice.apply(foo, [- 1, - 2, foo.length - 3]); + Uint8ClampedArray.prototype.splice.apply(foo, [foo.length - 1, foo.length - 2, foo.length - 3]); + Int16Array.prototype.slice.apply(foo, [- 1, - 2, foo.length - 3]); + Int16Array.prototype.splice.apply(foo, [foo.length - 1, foo.length - 2, foo.length - 3]); + Uint16Array.prototype.slice.apply(foo, [- 1, - 2, foo.length - 3]); + Uint16Array.prototype.splice.apply(foo, [foo.length - 1, foo.length - 2, foo.length - 3]); + Int32Array.prototype.slice.apply(foo, [- 1, - 2, foo.length - 3]); + Int32Array.prototype.splice.apply(foo, [foo.length - 1, foo.length - 2, foo.length - 3]); + Uint32Array.prototype.slice.apply(foo, [- 1, - 2, foo.length - 3]); + Uint32Array.prototype.splice.apply(foo, [foo.length - 1, foo.length - 2, foo.length - 3]); + Float32Array.prototype.slice.apply(foo, [- 1, - 2, foo.length - 3]); + Float32Array.prototype.splice.apply(foo, [foo.length - 1, foo.length - 2, foo.length - 3]); + Float64Array.prototype.slice.apply(foo, [- 1, - 2, foo.length - 3]); + Float64Array.prototype.splice.apply(foo, [foo.length - 1, foo.length - 2, foo.length - 3]); + BigInt64Array.prototype.slice.apply(foo, [- 1, - 2, foo.length - 3]); + BigInt64Array.prototype.splice.apply(foo, [foo.length - 1, foo.length - 2, foo.length - 3]); + BigUint64Array.prototype.slice.apply(foo, [- 1, - 2, foo.length - 3]); + BigUint64Array.prototype.splice.apply(foo, [foo.length - 1, foo.length - 2, foo.length - 3]); + NOT_SUPPORTED.prototype.slice.apply(foo, [foo.length - 1, foo.length - 2, foo.length - 3]); + NOT_SUPPORTED.prototype.splice.apply(foo, [foo.length - 1, foo.length - 2, foo.length - 3]); + ` + } + ] +});