Skip to content

Commit

Permalink
New: isParenthesized function
Browse files Browse the repository at this point in the history
  • Loading branch information
mysticatea committed Oct 5, 2018
1 parent 6123e16 commit 66456c5
Show file tree
Hide file tree
Showing 5 changed files with 386 additions and 0 deletions.
66 changes: 66 additions & 0 deletions docs/api/ast-utils.md
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,72 @@ function getStringIfConstant(node, initialScope) {

----

## isParenthesized

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

Check whether a given node is parenthesized or not.

This function detects it correctly even if it's parenthesized by specific syntax.

```js
f(a); //→ this `a` is not parenthesized.
f((b)); //→ this `b` is parenthesized.

new C(a); //→ this `a` is not parenthesized.
new C((b)); //→ this `b` is parenthesized.

if (a) {} //→ this `a` is not parenthesized.
if ((b)) {} //→ this `b` is parenthesized.

switch (a) {} //→ this `a` is not parenthesized.
switch ((b)) {} //→ this `b` is parenthesized.

while (a) {} //→ this `a` is not parenthesized.
while ((b)) {} //→ this `b` is parenthesized.

do {} while (a); //→ this `a` is not parenthesized.
do {} while ((b)); //→ this `b` is parenthesized.

with (a) {} //→ this `a` is not parenthesized.
with ((b)) {} //→ this `b` is parenthesized.
```

### Parameters

Name | Type | Description
:-----|:-----|:------------
node | Node | The node to check.
sourceCode | SourceCode | The source code object to get tokens.

### Return value

`true` if the node is parenthesized.

### Example

```js{9}
const { isParenthesized } = require("eslint-utils")
module.exports = {
meta: {},
create(context) {
const sourceCode = context.getSourceCode()
return {
":expression"(node) {
if (isParenthesized(node, sourceCode)) {
// ...
}
},
}
},
}
```

----

## PatternMatcher class

```js
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"devDependencies": {
"@mysticatea/eslint-plugin": "^5.0.1",
"codecov": "^3.0.2",
"dot-prop": "^4.2.0",
"eslint": "^5.0.1",
"esm": "^3.0.55",
"espree": "^4.0.0",
Expand Down
3 changes: 3 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { getInnermostScope } from "./get-innermost-scope"
import { getPropertyName } from "./get-property-name"
import { getStaticValue } from "./get-static-value"
import { getStringIfConstant } from "./get-string-if-constant"
import { isParenthesized } from "./is-parenthesized"
import { PatternMatcher } from "./pattern-matcher"
import {
CALL,
Expand Down Expand Up @@ -70,6 +71,7 @@ export default {
isOpeningBraceToken,
isOpeningBracketToken,
isOpeningParenToken,
isParenthesized,
isSemicolonToken,
PatternMatcher,
READ,
Expand Down Expand Up @@ -107,6 +109,7 @@ export {
isOpeningBraceToken,
isOpeningBracketToken,
isOpeningParenToken,
isParenthesized,
isSemicolonToken,
PatternMatcher,
READ,
Expand Down
79 changes: 79 additions & 0 deletions src/is-parenthesized.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { isClosingParenToken, isOpeningParenToken } from "./token-predicate"

/**
* Get the left parenthesis of the parent node syntax if it exists.
* E.g., `if (a) {}` then the `(`.
* @param {Node} node The AST node to check.
* @param {SourceCode} sourceCode The source code object to get tokens.
* @returns {Token|null} The left parenthesis of the parent node syntax
*/
function getParentSyntaxParen(node, sourceCode) {
const parent = node.parent

switch (parent.type) {
case "CallExpression":
case "NewExpression":
if (parent.arguments.length === 1 && parent.arguments[0] === node) {
return sourceCode.getTokenAfter(
parent.callee,
isOpeningParenToken
)
}
return null

case "DoWhileStatement":
if (parent.test === node) {
return sourceCode.getTokenAfter(
parent.body,
isOpeningParenToken
)
}
return null

case "IfStatement":
case "WhileStatement":
if (parent.test === node) {
return sourceCode.getFirstToken(parent, 1)
}
return null

case "SwitchStatement":
if (parent.discriminant === node) {
return sourceCode.getFirstToken(parent, 1)
}
return null

case "WithStatement":
if (parent.object === node) {
return sourceCode.getFirstToken(parent, 1)
}
return null

default:
return null
}
}

/**
* 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) {
if (node == null) {
return false
}

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

return (
maybeLeftParen != null &&
maybeRightParen != null &&
isOpeningParenToken(maybeLeftParen) &&
isClosingParenToken(maybeRightParen) &&
// Avoid false positive such as `if (a) {}`
maybeLeftParen !== getParentSyntaxParen(node, sourceCode)
)
}
Loading

0 comments on commit 66456c5

Please sign in to comment.