Skip to content

Commit

Permalink
✨ isParenthesized supports to check redundant parens
Browse files Browse the repository at this point in the history
  • Loading branch information
mysticatea committed Jul 14, 2019
1 parent 89194cb commit ebf5a83
Show file tree
Hide file tree
Showing 4 changed files with 117 additions and 7 deletions.
1 change: 1 addition & 0 deletions .eslintrc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ extends:
- plugin:@mysticatea/+node

rules:
init-declarations: "off"
"@mysticatea/node/no-unsupported-features/es-syntax":
- error
- ignores:
Expand Down
6 changes: 5 additions & 1 deletion docs/api/ast-utils.md
Original file line number Diff line number Diff line change
Expand Up @@ -414,7 +414,8 @@ module.exports = {
## isParenthesized

```js
const ret = utils.isParenthesized(node, sourceCode)
const ret1 = utils.isParenthesized(times, node, sourceCode)
const ret2 = utils.isParenthesized(node, sourceCode)
```

Check whether a given node is parenthesized or not.
Expand Down Expand Up @@ -445,13 +446,16 @@ with ((b)) {}

Name | Type | Description
:-----|:-----|:------------
times | number | Optional. The number of redundant parenthesized. Default is `1`.
node | Node | The node to check.
sourceCode | SourceCode | The source code object to get tokens.

### Return value

`true` if the node is parenthesized.

If `times` was given, it returns `true` only if the node is parenthesized the `times` times. For example, `isParenthesized(2, node, sourceCode)` returns `true` for `((foo))`, but not for `(foo)`.

### Example

```js{9}
Expand Down
41 changes: 35 additions & 6 deletions src/is-parenthesized.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,26 +54,55 @@ function getParentSyntaxParen(node, sourceCode) {
}
}

/**
* Check whether a given node is parenthesized or not.
* @param {number} times The number of parantheses.
* @param {Node} node The AST node to check.
* @param {SourceCode} sourceCode The source code object to get tokens.
* @returns {boolean} `true` if the node is parenthesized the given times.
*/
/**
* Check whether a given node is parenthesized or not.
* @param {Node} node The AST node to check.
* @param {SourceCode} sourceCode The source code object to get tokens.
* @returns {boolean} `true` if the node is parenthesized.
*/
export function isParenthesized(node, sourceCode) {
export function isParenthesized(
timesOrNode,
nodeOrSourceCode,
optionalSourceCode
) {
let times, node, sourceCode, maybeLeftParen, maybeRightParen
if (typeof timesOrNode === "number") {
times = timesOrNode | 0
node = nodeOrSourceCode
sourceCode = optionalSourceCode
if (!(times >= 1)) {
throw new TypeError("'times' should be a positive integer.")
}
} else {
times = 1
node = timesOrNode
sourceCode = nodeOrSourceCode
}

if (node == null) {
return false
}

const maybeLeftParen = sourceCode.getTokenBefore(node)
const maybeRightParen = sourceCode.getTokenAfter(node)

return (
maybeLeftParen = maybeRightParen = node
do {
maybeLeftParen = sourceCode.getTokenBefore(maybeLeftParen)
maybeRightParen = sourceCode.getTokenAfter(maybeRightParen)
} while (
maybeLeftParen != null &&
maybeRightParen != null &&
isOpeningParenToken(maybeLeftParen) &&
isClosingParenToken(maybeRightParen) &&
// Avoid false positive such as `if (a) {}`
maybeLeftParen !== getParentSyntaxParen(node, sourceCode)
maybeLeftParen !== getParentSyntaxParen(node, sourceCode) &&
--times > 0
)

return times === 0
}
76 changes: 76 additions & 0 deletions test/is-parenthesized.js
Original file line number Diff line number Diff line change
Expand Up @@ -234,4 +234,80 @@ describe("The 'isParenthesized' function", () => {
}
})
}

for (const { code, expected } of [
{
code: "777",
expected: {
"body.0": false,
"body.0.expression": false,
},
},
{
code: "(777)",
expected: {
"body.0": false,
"body.0.expression": false,
},
},
{
code: "((777))",
expected: {
"body.0": false,
"body.0.expression": true,
},
},
{
code: "if (a) ;",
expected: {
"body.0": false,
"body.0.test": false,
},
},
{
code: "if ((a)) ;",
expected: {
"body.0": false,
"body.0.test": false,
},
},
{
code: "if (((a))) ;",
expected: {
"body.0": false,
"body.0.test": true,
},
},
]) {
describe(`on the code \`${code}\` and 2 times`, () => {
for (const key of Object.keys(expected)) {
it(`should return ${expected[key]} at "${key}"`, () => {
const linter = new eslint.Linter()

let actual = null
linter.defineRule("test", context => ({
Program(node) {
actual = isParenthesized(
2,
dotProp.get(node, key),
context.getSourceCode()
)
},
}))
const messages = linter.verify(code, {
env: { es6: true },
parserOptions: { ecmaVersion: 2018 },
rules: { test: "error" },
})

assert.strictEqual(
messages.length,
0,
messages[0] && messages[0].message
)
assert.strictEqual(actual, expected[key])
})
}
})
}
})

0 comments on commit ebf5a83

Please sign in to comment.