Skip to content

Commit 9fb19d6

Browse files
authored
Fix false positive for well-known symbols in es-x/no-nonstandard-*-properties rules (#231)
1 parent c3e36e2 commit 9fb19d6

File tree

5 files changed

+89
-14
lines changed

5 files changed

+89
-14
lines changed

lib/util/define-nonstandard-prototype-properties-handler/index.js

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"use strict"
22

3-
const { getPropertyName } = require("@eslint-community/eslint-utils")
3+
const { getPropertyKeyValue } = require("../get-property-key-value")
44
const {
55
buildObjectTypeChecker,
66
} = require("../type-checker/object-type-checker")
@@ -48,12 +48,13 @@ function defineNonstandardPrototypePropertiesHandler(
4848

4949
return {
5050
MemberExpression(node) {
51-
const propertyName = getPropertyName(
51+
const propertyName = getPropertyKeyValue(
5252
node,
5353
sourceCode.getScope(node),
5454
)
5555
if (
56-
propertyName == null ||
56+
// If the key is a symbol, it is ignored.
57+
typeof propertyName !== "string" ||
5758
options?.allowsPropertyName?.(propertyName)
5859
) {
5960
return
@@ -75,12 +76,13 @@ function defineNonstandardPrototypePropertiesHandler(
7576
"AssignmentExpression > ObjectPattern.left > Property.properties",
7677
"AssignmentPattern > ObjectPattern.left > Property.properties",
7778
].join(",")](node) {
78-
const propertyName = getPropertyName(
79+
const propertyName = getPropertyKeyValue(
7980
node,
8081
sourceCode.getScope(node),
8182
)
8283
if (
83-
propertyName == null ||
84+
// If the key is a symbol, it is ignored.
85+
typeof propertyName !== "string" ||
8486
options?.allowsPropertyName?.(propertyName)
8587
) {
8688
return

lib/util/define-nonstandard-static-properties-handler/index.js

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
11
"use strict"
22

3-
const {
4-
getPropertyName,
5-
ReferenceTracker,
6-
READ,
7-
} = require("@eslint-community/eslint-utils")
3+
const { getPropertyKeyValue } = require("../get-property-key-value")
4+
const { ReferenceTracker, READ } = require("@eslint-community/eslint-utils")
85
const { getSourceCode } = require("eslint-compat-utils")
96

107
/**
@@ -51,11 +48,15 @@ function defineNonstandardStaticPropertiesHandler(context, nameMap) {
5148
if (prop.type !== "Property") {
5249
continue
5350
}
54-
const propertyName = getPropertyName(
51+
const propertyName = getPropertyKeyValue(
5552
prop,
5653
sourceCode.getScope(node),
5754
)
58-
if (propertyName == null || propertyNames.has(propertyName)) {
55+
if (
56+
// If the key is a symbol, it is ignored.
57+
typeof propertyName !== "string" ||
58+
propertyNames.has(propertyName)
59+
) {
5960
continue
6061
}
6162
yield { node: prop, propertyName }
@@ -74,11 +75,15 @@ function defineNonstandardStaticPropertiesHandler(context, nameMap) {
7475
if (parent.object !== node) {
7576
continue
7677
}
77-
const propertyName = getPropertyName(
78+
const propertyName = getPropertyKeyValue(
7879
parent,
7980
sourceCode.getScope(node),
8081
)
81-
if (propertyName == null || propertyNames.has(propertyName)) {
82+
if (
83+
// If the key is a symbol, it is ignored.
84+
typeof propertyName !== "string" ||
85+
propertyNames.has(propertyName)
86+
) {
8287
continue
8388
}
8489
report(parent, path, propertyName)

lib/util/get-property-key-value.js

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
"use strict"
2+
3+
const { getStaticValue } = require("@eslint-community/eslint-utils")
4+
5+
/**
6+
* @typedef {import('estree').Property} Property
7+
* @typedef {import('estree').MemberExpression} MemberExpression
8+
* @typedef {import('estree').Expression} Expression
9+
* @typedef {import('estree').PrivateIdentifier} PrivateIdentifier
10+
* @typedef {import('eslint').Scope.Scope} Scope
11+
*/
12+
/**
13+
* Get the property name/symbol from a MemberExpression node or a Property node.
14+
*
15+
* The difference from `@eslint-community/eslint-utils.getPropertyName()` is
16+
* that if key is a Symbol, this function returns a Symbol.
17+
* @param {Property|MemberExpression} node The node to get.
18+
* @param {Scope} [initialScope] The scope to start finding variable. Optional. If the node is a computed property node and this scope was given, this checks the computed property name by the `getStringIfConstant` function with the scope, and returns the value of it.
19+
* @returns {string|symbol|null}
20+
*/
21+
function getPropertyKeyValue(node, initialScope) {
22+
switch (node.type) {
23+
case "MemberExpression":
24+
if (node.computed) {
25+
return getStaticKeyValue(node.property, initialScope)
26+
}
27+
if (node.property.type === "PrivateIdentifier") {
28+
return null
29+
}
30+
return node.property.name
31+
32+
case "Property":
33+
case "MethodDefinition":
34+
case "PropertyDefinition":
35+
if (node.computed) {
36+
return getStaticKeyValue(node.key, initialScope)
37+
}
38+
if (node.key.type === "Literal") {
39+
return String(node.key.value)
40+
}
41+
if (node.key.type === "PrivateIdentifier") {
42+
return null
43+
}
44+
return node.key.name
45+
46+
// no default
47+
}
48+
49+
return null
50+
}
51+
52+
/**
53+
* @param {Expression|PrivateIdentifier} node
54+
* @param {Scope} initialScope
55+
*/
56+
function getStaticKeyValue(node, initialScope) {
57+
const value = getStaticValue(node, initialScope)
58+
return value && typeof value.value === "symbol"
59+
? value.value
60+
: String(value.value)
61+
}
62+
63+
module.exports = { getPropertyKeyValue }

tests/lib/rules/no-nonstandard-array-properties.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ const { arrayProperties } = require("../../../lib/util/well-known-properties")
77
new RuleTester().run("no-nonstandard-array-properties", rule, {
88
valid: [
99
...[...arrayProperties].map((p) => `Array.${p}`),
10+
"Array[Symbol.species]",
11+
"const {[Symbol.species]:foo} = Array",
1012
{ code: "Array.unknown()", options: [{ allow: ["unknown"] }] },
1113
],
1214
invalid: [

tests/lib/rules/no-nonstandard-array-prototype-properties.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ new RuleTester().run(ruleId, rule, {
1616
...[...arrayPrototypeProperties].map((p) => `['A'].${p}`),
1717
"['A'][0]",
1818
"['A']['0']",
19+
"['A'][Symbol.iterator]",
20+
"['A'][Symbol.unscopables]",
21+
"const {[Symbol.iterator]:iterator} = []",
1922
{ code: "['A'].unknown()", options: [{ allow: ["unknown"] }] },
2023
// Test for https://github.com/eslint-community/eslint-plugin-es-x/issues/223
2124
"for (const { x } of foo) {}",

0 commit comments

Comments
 (0)