Skip to content

Commit

Permalink
Update: add fixer for no-extra-parens (fixes #6944) (#6950)
Browse files Browse the repository at this point in the history
  • Loading branch information
not-an-aardvark authored and nzakas committed Aug 26, 2016
1 parent ca3d448 commit c210510
Show file tree
Hide file tree
Showing 3 changed files with 255 additions and 124 deletions.
2 changes: 2 additions & 0 deletions docs/rules/no-extra-parens.md
@@ -1,5 +1,7 @@
# disallow unnecessary parentheses (no-extra-parens)

(fixable) The `--fix` option on the [command line](../user-guide/command-line-interface#fix) automatically fixes problems reported by this rule.

This rule restricts the use of parentheses to only where they are necessary.

## Rule Details
Expand Down
57 changes: 54 additions & 3 deletions lib/rules/no-extra-parens.js
Expand Up @@ -9,6 +9,7 @@
//------------------------------------------------------------------------------

const astUtils = require("../ast-utils.js");
const esUtils = require("esutils");

module.exports = {
meta: {
Expand All @@ -18,6 +19,8 @@ module.exports = {
recommended: false
},

fixable: "code",

schema: {
anyOf: [
{
Expand Down Expand Up @@ -256,16 +259,64 @@ module.exports = {
throw new Error("unreachable");
}

/**
* Determines whether a node should be preceded by an additional space when removing parens
* @param {ASTNode} node node to evaluate; must be surrounded by parentheses
* @returns {boolean} `true` if a space should be inserted before the node
* @private
*/
function requiresLeadingSpace(node) {
const leftParenToken = sourceCode.getTokenBefore(node);
const tokenBeforeLeftParen = sourceCode.getTokenBefore(node, 1);
const firstToken = sourceCode.getFirstToken(node);

// If there is already whitespace before the previous token, don't add more.
if (!tokenBeforeLeftParen || tokenBeforeLeftParen.end !== leftParenToken.start) {
return false;
}

// If the parens are preceded by a keyword (e.g. `typeof(0)`), a space should be inserted (`typeof 0`)
const precededByKeyword = tokenBeforeLeftParen.type === "Keyword";

// However, a space should not be inserted unless the first character of the token is an identifier part
// e.g. `typeof([])` should be fixed to `typeof[]`
const startsWithIdentifierPart = esUtils.code.isIdentifierPartES6(firstToken.value.charCodeAt(0));

// If the parens are preceded by and start with a unary plus/minus (e.g. `+(+foo)`), a space should be inserted (`+ +foo`)
const precededByUnaryPlus = tokenBeforeLeftParen.type === "Punctuator" && tokenBeforeLeftParen.value === "+";
const precededByUnaryMinus = tokenBeforeLeftParen.type === "Punctuator" && tokenBeforeLeftParen.value === "-";

const startsWithUnaryPlus = firstToken.type === "Punctuator" && firstToken.value === "+";
const startsWithUnaryMinus = firstToken.type === "Punctuator" && firstToken.value === "-";

return (precededByKeyword && startsWithIdentifierPart) ||
(precededByUnaryPlus && startsWithUnaryPlus) ||
(precededByUnaryMinus && startsWithUnaryMinus);
}

/**
* Report the node
* @param {ASTNode} node node to evaluate
* @returns {void}
* @private
*/
function report(node) {
const previousToken = sourceCode.getTokenBefore(node);

context.report(node, previousToken.loc.start, "Gratuitous parentheses around expression.");
const leftParenToken = sourceCode.getTokenBefore(node);
const rightParenToken = sourceCode.getTokenAfter(node);

context.report({
node,
loc: leftParenToken.loc.start,
message: "Gratuitous parentheses around expression.",
fix(fixer) {
const parenthesizedSource = sourceCode.text.slice(leftParenToken.range[1], rightParenToken.range[0]);

return fixer.replaceTextRange([
leftParenToken.range[0],
rightParenToken.range[1]
], (requiresLeadingSpace(node) ? " " : "") + parenthesizedSource);
}
});
}

/**
Expand Down

0 comments on commit c210510

Please sign in to comment.