Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: check assignment patterns in no-underscore-dangle #16693

Merged
merged 1 commit into from Dec 28, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 0 additions & 1 deletion docs/src/rules/no-underscore-dangle.md
Expand Up @@ -28,7 +28,6 @@ Examples of **incorrect** code for this rule:
var foo_;
var __proto__ = {};
foo._bar();
const [_foo, ..._bar] = list;
```
Comment on lines 28 to 31
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's present in markdown but I don't see it in the latest deployed docs,

Screenshot 2022-12-24 at 3 57 27 PM

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was added in #16006 which was merged after the release. It's visible in the head version:

https://eslint.org/docs/head/rules/no-underscore-dangle


:::
Expand Down
102 changes: 25 additions & 77 deletions lib/rules/no-underscore-dangle.js
Expand Up @@ -205,60 +205,6 @@ module.exports = {
checkForDanglingUnderscoreInFunctionParameters(node);
}

/**
* Check if node has dangling underscore or if node is type of ArrayPattern check its elements recursively
* @param {ASTNode} node node to evaluate
* @param {string} parentNodeType the ASTNode['type'] of the node parent
* @returns {void}
* @private
*/
function deepCheckDestructured(node, parentNodeType) {
let identifier;

if (!node || !node.type) {
return;
}

switch (node.type) {
case "ArrayPattern":
node.elements.forEach(element => deepCheckDestructured(element, "ArrayPattern"));
break;
case "ObjectPattern":
node.properties.forEach(property => deepCheckDestructured(property, "ObjectPattern"));
break;
case "RestElement":
deepCheckDestructured(node.argument, parentNodeType);
break;
case "Property":
deepCheckDestructured(node.value, "ObjectPattern");
break;
case "Identifier":
identifier = node.name;
break;
default:
break;
}

const isFromDestructuredObject = parentNodeType === "ObjectPattern" && !allowInObjectDestructuring;
const isFromDestructuredArray = parentNodeType === "ArrayPattern" && !allowInArrayDestructuring;
const hasDisallowedDestructuring = isFromDestructuredObject || isFromDestructuredArray;

if (
identifier &&
hasDisallowedDestructuring &&
hasDanglingUnderscore(identifier) &&
!isSpecialCaseIdentifierInVariableExpression(identifier) &&
!isAllowed(identifier)
) {
context.report({
node,
messageId: "unexpectedUnderscore",
data: {
identifier
}
});
}
}

/**
* Check if variable expression has a dangling underscore
Expand All @@ -267,30 +213,32 @@ module.exports = {
* @private
*/
function checkForDanglingUnderscoreInVariableExpression(node) {
if (node.id.type === "ArrayPattern") {
node.id.elements.forEach(element => {
deepCheckDestructured(element, node.id.type);
});
}

if (node.id.type === "ObjectPattern") {
node.id.properties.forEach(element => {
deepCheckDestructured(element, node.id.type);
});
}

const identifier = node.id.name;
context.getDeclaredVariables(node).forEach(variable => {
const definition = variable.defs.find(def => def.node === node);
const identifierNode = definition.name;
const identifier = identifierNode.name;
let parent = identifierNode.parent;

while (!["VariableDeclarator", "ArrayPattern", "ObjectPattern"].includes(parent.type)) {
parent = parent.parent;
}

if (typeof identifier !== "undefined" && hasDanglingUnderscore(identifier) &&
!isSpecialCaseIdentifierInVariableExpression(identifier) && !isAllowed(identifier)) {
context.report({
node,
messageId: "unexpectedUnderscore",
data: {
identifier
}
});
}
if (
hasDanglingUnderscore(identifier) &&
!isSpecialCaseIdentifierInVariableExpression(identifier) &&
!isAllowed(identifier) &&
!(allowInArrayDestructuring && parent.type === "ArrayPattern") &&
!(allowInObjectDestructuring && parent.type === "ObjectPattern")
) {
context.report({
node,
messageId: "unexpectedUnderscore",
data: {
identifier
}
});
}
});
}

/**
Expand Down
21 changes: 21 additions & 0 deletions tests/lib/rules/no-underscore-dangle.js
Expand Up @@ -70,8 +70,14 @@ ruleTester.run("no-underscore-dangle", rule, {
{ code: "function foo( { _bar }) {}", options: [{ allowFunctionParams: false }], parserOptions: { ecmaVersion: 6 } },
{ code: "function foo( { _bar = 0 } = {}) {}", options: [{ allowFunctionParams: false }], parserOptions: { ecmaVersion: 6 } },
{ code: "function foo(...[_bar]) {}", options: [{ allowFunctionParams: false }], parserOptions: { ecmaVersion: 2016 } },
{ code: "const [_foo] = arr", parserOptions: { ecmaVersion: 6 } },
{ code: "const [_foo] = arr", options: [{}], parserOptions: { ecmaVersion: 6 } },
{ code: "const [_foo] = arr", options: [{ allowInArrayDestructuring: true }], parserOptions: { ecmaVersion: 6 } },
{ code: "const [foo, ...rest] = [1, 2, 3]", options: [{ allowInArrayDestructuring: false }], parserOptions: { ecmaVersion: 2022 } },
{ code: "const [foo, _bar] = [1, 2, 3]", options: [{ allowInArrayDestructuring: false, allow: ["_bar"] }], parserOptions: { ecmaVersion: 2022 } },
{ code: "const { _foo } = obj", parserOptions: { ecmaVersion: 6 } },
{ code: "const { _foo } = obj", options: [{}], parserOptions: { ecmaVersion: 6 } },
{ code: "const { _foo } = obj", options: [{ allowInObjectDestructuring: true }], parserOptions: { ecmaVersion: 6 } },
{ code: "const { foo, bar: _bar } = { foo: 1, bar: 2 }", options: [{ allowInObjectDestructuring: false, allow: ["_bar"] }], parserOptions: { ecmaVersion: 2022 } },
{ code: "const { foo, _bar } = { foo: 1, _bar: 2 }", options: [{ allowInObjectDestructuring: false, allow: ["_bar"] }], parserOptions: { ecmaVersion: 2022 } },
{ code: "const { foo, _bar: bar } = { foo: 1, _bar: 2 }", options: [{ allowInObjectDestructuring: false }], parserOptions: { ecmaVersion: 2022 } },
Expand Down Expand Up @@ -112,6 +118,11 @@ ruleTester.run("no-underscore-dangle", rule, {
options: [{ allowInArrayDestructuring: false }],
parserOptions: { ecmaVersion: 2022 },
errors: [{ messageId: "unexpectedUnderscore", data: { identifier: "_bar" } }]
}, {
code: "const [_foo = 1] = arr",
options: [{ allowInArrayDestructuring: false }],
parserOptions: { ecmaVersion: 2022 },
errors: [{ messageId: "unexpectedUnderscore", data: { identifier: "_foo" } }]
}, {
code: "const [foo, ..._rest] = [1, 2, 3]",
options: [{ allowInArrayDestructuring: false }],
Expand All @@ -127,6 +138,16 @@ ruleTester.run("no-underscore-dangle", rule, {
options: [{ allowInObjectDestructuring: false }],
parserOptions: { ecmaVersion: 2022 },
errors: [{ messageId: "unexpectedUnderscore", data: { identifier: "_foo" } }]
}, {
code: "const { _foo = 1 } = obj",
options: [{ allowInObjectDestructuring: false }],
parserOptions: { ecmaVersion: 2022 },
errors: [{ messageId: "unexpectedUnderscore", data: { identifier: "_foo" } }]
}, {
code: "const { bar: _foo = 1 } = obj",
options: [{ allowInObjectDestructuring: false }],
parserOptions: { ecmaVersion: 2022 },
errors: [{ messageId: "unexpectedUnderscore", data: { identifier: "_foo" } }]
}, {
code: "const { foo: _foo, bar } = { foo: 1, bar: 2 }",
options: [{ allowInObjectDestructuring: false }],
Expand Down