Skip to content

Commit

Permalink
New: prefer-rest-params rule (fixes eslint#4108)
Browse files Browse the repository at this point in the history
  • Loading branch information
mysticatea committed Nov 23, 2015
1 parent cbdc73b commit 8424eca
Show file tree
Hide file tree
Showing 5 changed files with 157 additions and 1 deletion.
3 changes: 2 additions & 1 deletion conf/eslint.json
Expand Up @@ -163,8 +163,9 @@
"padded-blocks": 0,
"prefer-arrow-callback": 0,
"prefer-const": 0,
"prefer-spread": 0,
"prefer-reflect": 0,
"prefer-rest-params": 0,
"prefer-spread": 0,
"prefer-template": 0,
"quote-props": 0,
"quotes": [0, "double"],
Expand Down
1 change: 1 addition & 0 deletions docs/rules/README.md
Expand Up @@ -224,6 +224,7 @@ These rules are only relevant to ES6 environments.
* [prefer-arrow-callback](prefer-arrow-callback.md) - suggest using arrow functions as callbacks
* [prefer-const](prefer-const.md) - suggest using `const` declaration for variables that are never modified after declared
* [prefer-reflect](prefer-reflect.md) - suggest using Reflect methods where applicable
* [prefer-rest-params](prefer-rest-params.md) - suggest using the rest parameters instead of `arguments`
* [prefer-spread](prefer-spread.md) - suggest using the spread operator instead of `.apply()`.
* [prefer-template](prefer-template.md) - suggest using template literals instead of strings concatenation
* [require-yield](require-yield.md) - disallow generator functions that do not have `yield`
Expand Down
54 changes: 54 additions & 0 deletions docs/rules/prefer-rest-params.md
@@ -0,0 +1,54 @@
# Suggest using the rest parameters instead of `arguments` (prefer-rest-params)

There is rest parameters in ES2015.
We can use that feature for variadic functions instead of `arguments` variable.

`arguments` does not have methods of `Array.prototype`, so it's inconvenience a bit.

## Rule Details

This rule is aimed to flag usage of `arguments` variables.

The following patterns are considered problems:

```js
function foo() {
console.log(arguments); /*error Use the rest parameters instead of "arguments". */
}

function foo(action) {
var args = [].slice.call(arguments, 1); /*error Use the rest parameters instead of "arguments". */
action.apply(null, args);
}
```

The following patterns are considered not problems:

```js
function foo(...args) {
console.log(args);
}

function foo(action, ...args) {
action.apply(null, args); // or `action(...args)`, related with the `prefer-spread` rule.
}

// Note: the implicit arguments can be overwritten.
function foo(arguments) {
console.log(arguments); // This is the first argument.
}
function foo() {
var arguments = 0;
console.log(arguments); // This is a local variable.
}
```

## When Not To Use It

This rule should not be used in ES3/5 environments.

In ES2015 (ES6) or later, if you don't want to be notified about `arguments` variables, then it's safe to disable this rule.

## Related rules

* [prefer-spread](prefer-spread.md)
71 changes: 71 additions & 0 deletions lib/rules/prefer-rest-params.js
@@ -0,0 +1,71 @@
/**
* @fileoverview Rule to
* @author Toru Nagashima
* @copyright 2015 Toru Nagashima. All rights reserved.
* See LICENSE file in root directory for full license.
*/

"use strict";

//------------------------------------------------------------------------------
// Helpers
//------------------------------------------------------------------------------

/**
* Gets the variable object of `arguments` which is defined implicitly.
* @param {escope.Scope} scope - A scope to get.
* @returns {escope.Variable} The found variable object.
*/
function getVariableOfArguments(scope) {
var variables = scope.variables;
for (var i = 0; i < variables.length; ++i) {
var variable = variables[i];
if (variable.name === "arguments") {
// If there was a parameter which is named "arguments", the implicit "arguments" is not defined.
// So does fast return with null.
return (variable.identifiers.length === 0) ? variable : null;
}
}

/* istanbul ignore next : unreachable */
return null;
}

//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------

module.exports = function(context) {

/**
* Reports a given reference.
*
* @param {escope.Reference} reference - A reference to report.
* @returns {void}
*/
function report(reference) {
context.report({
node: reference.identifier,
message: "Use the rest parameters instead of \"arguments\"."
});
}

/**
* Reports references of the implicit `arguments` variable if exist.
*
* @returns {void}
*/
function checkForArguments() {
var argumentsVar = getVariableOfArguments(context.getScope());
if (argumentsVar) {
argumentsVar.references.forEach(report);
}
}

return {
FunctionDeclaration: checkForArguments,
FunctionExpression: checkForArguments
};
};

module.exports.schema = [];
29 changes: 29 additions & 0 deletions tests/lib/rules/prefer-rest-params.js
@@ -0,0 +1,29 @@
/**
* @fileoverview Tests for prefer-rest-params rule.
* @author Toru Nagashima
* @copyright 2015 Toru Nagashima. All rights reserved.
* See LICENSE file in root directory for full license.
*/

"use strict";

//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------

var rule = require("../../../lib/rules/prefer-rest-params"),
RuleTester = require("../../../lib/testers/rule-tester");

var ruleTester = new RuleTester();
ruleTester.run("prefer-rest-params", rule, {
valid: [
"arguments;",
"function foo(arguments) { arguments; }",
"function foo() { var arguments; arguments; }",
{code: "var foo = () => arguments;", ecmaFeatures: {arrowFunctions: true}}, // Arrows don't have "arguments".,
{code: "function foo(...args) { args; }", ecmaFeatures: {restParams: true}}
],
invalid: [
{code: "function foo() { arguments; }", errors: [{type: "Identifier", message: "Use the rest parameters instead of \"arguments\"."}]}
]
});

0 comments on commit 8424eca

Please sign in to comment.