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: fix no-restricted-properties false negatives with unknown objects #17818

Merged
merged 8 commits into from Dec 14, 2023
6 changes: 5 additions & 1 deletion docs/src/rules/no-restricted-properties.md
Expand Up @@ -11,7 +11,7 @@ Certain properties on objects may be disallowed in a codebase. This is useful fo

## Rule Details

This rule looks for accessing a given property key on a given object name, either when reading the property's value or invoking it as a function. You may specify an optional message to indicate an alternative API or a reason for the restriction.
This rule looks for accessing a given property key on a given object name, either when reading the property's value or invoking it as a function. You may specify an optional message to indicate an alternative API or a reason for the restriction. This rule applies to both properties accessed by dot notation and destructuring.

### Options

Expand Down Expand Up @@ -96,6 +96,10 @@ disallowedObjectName.disallowedPropertyName(); /*error Disallowed object propert
}] */

foo.__defineGetter__(bar, baz);

const { __defineGetter__ } = qux();

({ __defineGetter__ }) => {};
```

:::
Expand Down
43 changes: 15 additions & 28 deletions lib/rules/no-restricted-properties.js
Expand Up @@ -142,40 +142,27 @@ module.exports = {
}
}

/**
* Checks property accesses in a destructuring assignment expression, e.g. `var foo; ({foo} = bar);`
* @param {ASTNode} node An AssignmentExpression or AssignmentPattern node
* @returns {undefined}
*/
function checkDestructuringAssignment(node) {
if (node.right.type === "Identifier") {
const objectName = node.right.name;

if (node.left.type === "ObjectPattern") {
node.left.properties.forEach(property => {
checkPropertyAccess(node.left, objectName, astUtils.getStaticPropertyName(property));
});
}
}
}

return {
MemberExpression(node) {
checkPropertyAccess(node, node.object && node.object.name, astUtils.getStaticPropertyName(node));
},
VariableDeclarator(node) {
if (node.init && node.init.type === "Identifier") {
const objectName = node.init.name;

if (node.id.type === "ObjectPattern") {
node.id.properties.forEach(property => {
checkPropertyAccess(node.id, objectName, astUtils.getStaticPropertyName(property));
});
ObjectPattern(node) {
let objectName = null;

if (node.parent.type === "VariableDeclarator") {
if (node.parent.init && node.parent.init.type === "Identifier") {
objectName = node.parent.init.name;
}
} else if (node.parent.type === "AssignmentExpression" || node.parent.type === "AssignmentPattern") {
if (node.parent.right.type === "Identifier") {
objectName = node.parent.right.name;
}
}
},
AssignmentExpression: checkDestructuringAssignment,
AssignmentPattern: checkDestructuringAssignment

node.properties.forEach(property => {
checkPropertyAccess(node, objectName, astUtils.getStaticPropertyName(property));
});
}
};
}
};
132 changes: 132 additions & 0 deletions tests/lib/rules/no-restricted-properties.js
Expand Up @@ -546,6 +546,138 @@ ruleTester.run("no-restricted-properties", rule, {
},
type: "MemberExpression"
}]
}, {
code: "const { bar: { bad } = {} } = foo;",
options: [{ property: "bad" }],
languageOptions: { ecmaVersion: 6 },
errors: [{
messageId: "restrictedProperty",
data: {
propertyName: "bad",
message: ""
}
}]
}, {
code: "const { bar: { bad } } = foo;",
options: [{ property: "bad" }],
languageOptions: { ecmaVersion: 6 },
errors: [{
messageId: "restrictedProperty",
data: {
propertyName: "bad",
message: ""
}
}]
}, {
code: "const { bad } = foo();",
options: [{ property: "bad" }],
languageOptions: { ecmaVersion: 6 },
errors: [{
messageId: "restrictedProperty",
data: {
propertyName: "bad",
message: ""
}
}]
}, {
code: "({ bad } = foo());",
options: [{ property: "bad" }],
languageOptions: { ecmaVersion: 6 },
errors: [{
messageId: "restrictedProperty",
data: {
propertyName: "bad",
message: ""
}
}]
}, {
code: "({ bar: { bad } } = foo);",
options: [{ property: "bad" }],
languageOptions: { ecmaVersion: 6 },
errors: [{
messageId: "restrictedProperty",
data: {
propertyName: "bad",
message: ""
}
}]
}, {
code: "({ bar: { bad } = {} } = foo);",
options: [{ property: "bad" }],
languageOptions: { ecmaVersion: 6 },
errors: [{
messageId: "restrictedProperty",
data: {
propertyName: "bad",
message: ""
}
}]
}, {
code: "({ bad }) => {};",
options: [{ property: "bad" }],
languageOptions: { ecmaVersion: 6 },
errors: [{
messageId: "restrictedProperty",
data: {
propertyName: "bad",
message: ""
}
}]
}, {
code: "({ bad } = {}) => {};",
options: [{ property: "bad" }],
languageOptions: { ecmaVersion: 6 },
errors: [{
messageId: "restrictedProperty",
data: {
propertyName: "bad",
message: ""
}
}]
}, {
code: "({ bad: bar }) => {};",
options: [{ property: "bad" }],
languageOptions: { ecmaVersion: 6 },
errors: [{
messageId: "restrictedProperty",
data: {
propertyName: "bad",
message: ""
}
}]
}, {
code: "({ bar: { bad } = {} }) => {};",
options: [{ property: "bad" }],
languageOptions: { ecmaVersion: 6 },
errors: [{
messageId: "restrictedProperty",
data: {
propertyName: "bad",
message: ""
}
}]
}, {
code: "[{ bad }] = foo;",
options: [{ property: "bad" }],
languageOptions: { ecmaVersion: 6 },
errors: [{
messageId: "restrictedProperty",
data: {
propertyName: "bad",
message: ""
}
}]
}, {
code: "const [{ bad }] = foo;",
options: [{ property: "bad" }],
languageOptions: { ecmaVersion: 6 },
errors: [{
messageId: "restrictedProperty",
data: {
propertyName: "bad",
message: ""
}
}]
}
]
});