Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Update: add enforceForFunctionPrototypeMethods option to no-extra-par…
…ens (#12895)
  • Loading branch information
mdjermanovic committed Jun 5, 2020
1 parent 27ef73f commit b735a48
Show file tree
Hide file tree
Showing 3 changed files with 377 additions and 3 deletions.
19 changes: 18 additions & 1 deletion docs/rules/no-extra-parens.md
Expand Up @@ -7,7 +7,7 @@ This rule restricts the use of parentheses to only where they are necessary.
This rule always ignores extra parentheses around the following:

* RegExp literals such as `(/abc/).test(var)` to avoid conflicts with the [wrap-regex](wrap-regex.md) rule
* immediately-invoked function expressions (also known as IIFEs) such as `var x = (function () {})();` and `((function foo() {return 1;})())` to avoid conflicts with the [wrap-iife](wrap-iife.md) rule
* immediately-invoked function expressions (also known as IIFEs) such as `var x = (function () {})();` and `var x = (function () {}());` to avoid conflicts with the [wrap-iife](wrap-iife.md) rule
* arrow function arguments to avoid conflicts with the [arrow-parens](arrow-parens.md) rule

## Options
Expand All @@ -26,6 +26,7 @@ This rule has an object option for exceptions to the `"all"` option:
* `"enforceForArrowConditionals": false` allows extra parentheses around ternary expressions which are the body of an arrow function
* `"enforceForSequenceExpressions": false` allows extra parentheses around sequence expressions
* `"enforceForNewInMemberExpressions": false` allows extra parentheses around `new` expressions in member expressions
* `"enforceForFunctionPrototypeMethods": false` allows extra parentheses around immediate `.call` and `.apply` method calls on function expressions and around function expressions in the same context.

### all

Expand Down Expand Up @@ -222,6 +223,22 @@ const quux = (new Bar())[baz];
(new Bar()).doSomething();
```

### enforceForFunctionPrototypeMethods

Examples of **correct** code for this rule with the `"all"` and `{ "enforceForFunctionPrototypeMethods": false }` options:

```js
/* eslint no-extra-parens: ["error", "all", { "enforceForFunctionPrototypeMethods": false }] */

const foo = (function () {}).call();

const bar = (function () {}).apply();

const baz = (function () {}.call());

const quux = (function () {}.apply());
```

### functions

Examples of **incorrect** code for this rule with the `"functions"` option:
Expand Down
30 changes: 28 additions & 2 deletions lib/rules/no-extra-parens.js
Expand Up @@ -51,7 +51,8 @@ module.exports = {
ignoreJSX: { enum: ["none", "all", "single-line", "multi-line"] },
enforceForArrowConditionals: { type: "boolean" },
enforceForSequenceExpressions: { type: "boolean" },
enforceForNewInMemberExpressions: { type: "boolean" }
enforceForNewInMemberExpressions: { type: "boolean" },
enforceForFunctionPrototypeMethods: { type: "boolean" }
},
additionalProperties: false
}
Expand Down Expand Up @@ -83,12 +84,28 @@ module.exports = {
context.options[1].enforceForSequenceExpressions === false;
const IGNORE_NEW_IN_MEMBER_EXPR = ALL_NODES && context.options[1] &&
context.options[1].enforceForNewInMemberExpressions === false;
const IGNORE_FUNCTION_PROTOTYPE_METHODS = ALL_NODES && context.options[1] &&
context.options[1].enforceForFunctionPrototypeMethods === false;

const PRECEDENCE_OF_ASSIGNMENT_EXPR = precedence({ type: "AssignmentExpression" });
const PRECEDENCE_OF_UPDATE_EXPR = precedence({ type: "UpdateExpression" });

let reportsBuffer;

/**
* Determines whether the given node is a `call` or `apply` method call, invoked directly on a `FunctionExpression` node.
* Example: function(){}.call()
* @param {ASTNode} node The node to be checked.
* @returns {boolean} True if the node is an immediate `call` or `apply` method call.
* @private
*/
function isImmediateFunctionPrototypeMethodCall(node) {
return node.type === "CallExpression" &&
node.callee.type === "MemberExpression" &&
node.callee.object.type === "FunctionExpression" &&
["call", "apply"].includes(astUtils.getStaticPropertyName(node.callee));
}

/**
* Determines if this rule should be enforced for a node given the current configuration.
* @param {ASTNode} node The node to be checked.
Expand Down Expand Up @@ -125,6 +142,10 @@ module.exports = {
return false;
}

if (isImmediateFunctionPrototypeMethodCall(node) && IGNORE_FUNCTION_PROTOTYPE_METHODS) {
return false;
}

return ALL_NODES || node.type === "FunctionExpression" || node.type === "ArrowFunctionExpression";
}

Expand Down Expand Up @@ -929,7 +950,12 @@ module.exports = {
LogicalExpression: checkBinaryLogical,

MemberExpression(node) {
const nodeObjHasExcessParens = hasExcessParens(node.object);
const nodeObjHasExcessParens = hasExcessParens(node.object) &&
!(
isImmediateFunctionPrototypeMethodCall(node.parent) &&
node.parent.callee === node &&
IGNORE_FUNCTION_PROTOTYPE_METHODS
);

if (
nodeObjHasExcessParens &&
Expand Down

0 comments on commit b735a48

Please sign in to comment.