Repository: eslint/eslint (JavaScript)

Issue:
use astUtils.getStaticPropertyName in no-prototype-builtins
<!--
    ESLint adheres to the [JS Foundation Code of Conduct](https://js.foundation/community/code-of-conduct).

    This template is for requesting a rule change. If you are here for another reason, please see below:

    1. To report a bug: https://eslint.org/docs/developer-guide/contributing/reporting-bugs
    2. To propose a new rule: https://eslint.org/docs/developer-guide/contributing/new-rules
    3. To request a change that is not a bug fix, rule change, or new rule: https://eslint.org/docs/developer-guide/contributing/changes
    4. If you have any questions, please stop by our chatroom: https://gitter.im/eslint/eslint

    Note that leaving sections blank will make it difficult for us to troubleshoot and we may have to close the issue.
-->

**What rule do you want to change?**

`no-prototype-builtins`

**Does this change cause the rule to produce more or fewer warnings?**

more

**How will the change be implemented? (New option, new default behavior, etc.)?**

new default behavior

**Please provide some example code that this change will affect:**

<!-- Put your code examples here -->
```js
/*eslint no-prototype-builtins: "error"*/

var hasBarProperty = foo["hasOwnProperty"]("bar");

var isPrototypeOfBar = foo['isPrototypeOf'](bar);

var barIsEnumerable = foo[`propertyIsEnumerable`]("bar");
```

**What does the rule currently do for this code?**

no errors

**What will the rule do after it's changed?**

3 errors

**Are you willing to submit a pull request to implement this change?**

Yes.


Discussion:
I would argue that this is a bug in the current rule.
I'd also agree with treating this issue as a bug.
when can we get it changed？
This is now accepted, and I agree we can treat it as a semver-minor bug fix.
I'm working on this.



Patch:
diff --git a/lib/rules/no-prototype-builtins.js b/lib/rules/no-prototype-builtins.js
index ccec86c30da0..c5e4d49259b8 100644
--- a/lib/rules/no-prototype-builtins.js
+++ b/lib/rules/no-prototype-builtins.js
@@ -46,15 +46,15 @@ module.exports = {
          */
         function disallowBuiltIns(node) {
 
-            // TODO: just use `astUtils.getStaticPropertyName(node.callee)`
             const callee = astUtils.skipChainExpression(node.callee);
 
-            if (callee.type !== "MemberExpression" || callee.computed) {
+            if (callee.type !== "MemberExpression") {
                 return;
             }
-            const propName = callee.property.name;
 
-            if (DISALLOWED_PROPS.indexOf(propName) > -1) {
+            const propName = astUtils.getStaticPropertyName(callee);
+
+            if (propName !== null && DISALLOWED_PROPS.indexOf(propName) > -1) {
                 context.report({
                     messageId: "prototypeBuildIn",
                     loc: callee.property.loc,


Test patch:
diff --git a/tests/lib/rules/no-prototype-builtins.js b/tests/lib/rules/no-prototype-builtins.js
index 8f57545bef92..a65b54d4d636 100644
--- a/tests/lib/rules/no-prototype-builtins.js
+++ b/tests/lib/rules/no-prototype-builtins.js
@@ -17,99 +17,156 @@ const rule = require("../../../lib/rules/no-prototype-builtins"),
 //------------------------------------------------------------------------------
 const ruleTester = new RuleTester();
 
-const valid = [
-    { code: "Object.prototype.hasOwnProperty.call(foo, 'bar')" },
-    { code: "Object.prototype.isPrototypeOf.call(foo, 'bar')" },
-    { code: "Object.prototype.propertyIsEnumerable.call(foo, 'bar')" },
-    { code: "Object.prototype.hasOwnProperty.apply(foo, ['bar'])" },
-    { code: "Object.prototype.isPrototypeOf.apply(foo, ['bar'])" },
-    { code: "Object.prototype.propertyIsEnumerable.apply(foo, ['bar'])" },
-    { code: "hasOwnProperty(foo, 'bar')" },
-    { code: "isPrototypeOf(foo, 'bar')" },
-    { code: "propertyIsEnumerable(foo, 'bar')" },
-    { code: "({}.hasOwnProperty.call(foo, 'bar'))" },
-    { code: "({}.isPrototypeOf.call(foo, 'bar'))" },
-    { code: "({}.propertyIsEnumerable.call(foo, 'bar'))" },
-    { code: "({}.hasOwnProperty.apply(foo, ['bar']))" },
-    { code: "({}.isPrototypeOf.apply(foo, ['bar']))" },
-    { code: "({}.propertyIsEnumerable.apply(foo, ['bar']))" }
-];
+ruleTester.run("no-prototype-builtins", rule, {
+    valid: [
+        "Object.prototype.hasOwnProperty.call(foo, 'bar')",
+        "Object.prototype.isPrototypeOf.call(foo, 'bar')",
+        "Object.prototype.propertyIsEnumerable.call(foo, 'bar')",
+        "Object.prototype.hasOwnProperty.apply(foo, ['bar'])",
+        "Object.prototype.isPrototypeOf.apply(foo, ['bar'])",
+        "Object.prototype.propertyIsEnumerable.apply(foo, ['bar'])",
+        "foo.hasOwnProperty",
+        "foo.hasOwnProperty.bar()",
+        "foo(hasOwnProperty)",
+        "hasOwnProperty(foo, 'bar')",
+        "isPrototypeOf(foo, 'bar')",
+        "propertyIsEnumerable(foo, 'bar')",
+        "({}.hasOwnProperty.call(foo, 'bar'))",
+        "({}.isPrototypeOf.call(foo, 'bar'))",
+        "({}.propertyIsEnumerable.call(foo, 'bar'))",
+        "({}.hasOwnProperty.apply(foo, ['bar']))",
+        "({}.isPrototypeOf.apply(foo, ['bar']))",
+        "({}.propertyIsEnumerable.apply(foo, ['bar']))",
+        "foo[hasOwnProperty]('bar')",
+        "foo['HasOwnProperty']('bar')",
+        { code: "foo[`isPrototypeOff`]('bar')", parserOptions: { ecmaVersion: 2015 } },
+        { code: "foo?.['propertyIsEnumerabl']('bar')", parserOptions: { ecmaVersion: 2020 } },
+        "foo[1]('bar')",
+        "foo[null]('bar')",
 
-const invalid = [
-    {
-        code: "foo.hasOwnProperty('bar')",
-        errors: [{
-            line: 1,
-            column: 5,
-            endLine: 1,
-            endColumn: 19,
-            messageId: "prototypeBuildIn",
-            data: { prop: "hasOwnProperty" },
-            type: "CallExpression"
-        }]
-    },
-    {
-        code: "foo.isPrototypeOf('bar')",
-        errors: [{
-            line: 1,
-            column: 5,
-            endLine: 1,
-            endColumn: 18,
-            messageId: "prototypeBuildIn",
-            data: { prop: "isPrototypeOf" },
-            type: "CallExpression"
-        }]
-    },
-    {
-        code: "foo.propertyIsEnumerable('bar')",
-        errors: [{
-            line: 1,
-            column: 5,
-            endLine: 1,
-            endColumn: 25,
-            messageId: "prototypeBuildIn",
-            data: { prop: "propertyIsEnumerable" }
-        }]
-    },
-    {
-        code: "foo.bar.hasOwnProperty('bar')",
-        errors: [{
-            line: 1,
-            column: 9,
-            endLine: 1,
-            endColumn: 23,
-            messageId: "prototypeBuildIn",
-            data: { prop: "hasOwnProperty" },
-            type: "CallExpression"
-        }]
-    },
-    {
-        code: "foo.bar.baz.isPrototypeOf('bar')",
-        errors: [{
-            line: 1,
-            column: 13,
-            endLine: 1,
-            endColumn: 26,
-            messageId: "prototypeBuildIn",
-            data: { prop: "isPrototypeOf" },
-            type: "CallExpression"
-        }]
-    },
+        // out of scope for this rule
+        "foo['hasOwn' + 'Property']('bar')",
+        { code: "foo[`hasOwnProperty${''}`]('bar')", parserOptions: { ecmaVersion: 2015 } }
+    ],
 
-    // Optional chaining
-    {
-        code: "foo?.hasOwnProperty('bar')",
-        parserOptions: { ecmaVersion: 2020 },
-        errors: [{ messageId: "prototypeBuildIn", data: { prop: "hasOwnProperty" } }]
-    },
-    {
-        code: "(foo?.hasOwnProperty)('bar')",
-        parserOptions: { ecmaVersion: 2020 },
-        errors: [{ messageId: "prototypeBuildIn", data: { prop: "hasOwnProperty" } }]
-    }
-];
+    invalid: [
+        {
+            code: "foo.hasOwnProperty('bar')",
+            errors: [{
+                line: 1,
+                column: 5,
+                endLine: 1,
+                endColumn: 19,
+                messageId: "prototypeBuildIn",
+                data: { prop: "hasOwnProperty" },
+                type: "CallExpression"
+            }]
+        },
+        {
+            code: "foo.isPrototypeOf('bar')",
+            errors: [{
+                line: 1,
+                column: 5,
+                endLine: 1,
+                endColumn: 18,
+                messageId: "prototypeBuildIn",
+                data: { prop: "isPrototypeOf" },
+                type: "CallExpression"
+            }]
+        },
+        {
+            code: "foo.propertyIsEnumerable('bar')",
+            errors: [{
+                line: 1,
+                column: 5,
+                endLine: 1,
+                endColumn: 25,
+                messageId: "prototypeBuildIn",
+                data: { prop: "propertyIsEnumerable" }
+            }]
+        },
+        {
+            code: "foo.bar.hasOwnProperty('bar')",
+            errors: [{
+                line: 1,
+                column: 9,
+                endLine: 1,
+                endColumn: 23,
+                messageId: "prototypeBuildIn",
+                data: { prop: "hasOwnProperty" },
+                type: "CallExpression"
+            }]
+        },
+        {
+            code: "foo.bar.baz.isPrototypeOf('bar')",
+            errors: [{
+                line: 1,
+                column: 13,
+                endLine: 1,
+                endColumn: 26,
+                messageId: "prototypeBuildIn",
+                data: { prop: "isPrototypeOf" },
+                type: "CallExpression"
+            }]
+        },
+        {
+            code: "foo['hasOwnProperty']('bar')",
+            errors: [{
+                line: 1,
+                column: 5,
+                endLine: 1,
+                endColumn: 21,
+                messageId: "prototypeBuildIn",
+                data: { prop: "hasOwnProperty" },
+                type: "CallExpression"
+            }]
+        },
+        {
+            code: "foo[`isPrototypeOf`]('bar').baz",
+            parserOptions: { ecmaVersion: 2015 },
+            errors: [{
+                line: 1,
+                column: 5,
+                endLine: 1,
+                endColumn: 20,
+                messageId: "prototypeBuildIn",
+                data: { prop: "isPrototypeOf" },
+                type: "CallExpression"
+            }]
+        },
+        {
+            code: String.raw`foo.bar["propertyIsEnumerable"]('baz')`,
+            errors: [{
+                line: 1,
+                column: 9,
+                endLine: 1,
+                endColumn: 31,
+                messageId: "prototypeBuildIn",
+                data: { prop: "propertyIsEnumerable" },
+                type: "CallExpression"
+            }]
+        },
 
-ruleTester.run("no-prototype-builtins", rule, {
-    valid,
-    invalid
+        // Optional chaining
+        {
+            code: "foo?.hasOwnProperty('bar')",
+            parserOptions: { ecmaVersion: 2020 },
+            errors: [{ messageId: "prototypeBuildIn", data: { prop: "hasOwnProperty" } }]
+        },
+        {
+            code: "(foo?.hasOwnProperty)('bar')",
+            parserOptions: { ecmaVersion: 2020 },
+            errors: [{ messageId: "prototypeBuildIn", data: { prop: "hasOwnProperty" } }]
+        },
+        {
+            code: "foo?.['hasOwnProperty']('bar')",
+            parserOptions: { ecmaVersion: 2020 },
+            errors: [{ messageId: "prototypeBuildIn", data: { prop: "hasOwnProperty" } }]
+        },
+        {
+            code: "(foo?.[`hasOwnProperty`])('bar')",
+            parserOptions: { ecmaVersion: 2020 },
+            errors: [{ messageId: "prototypeBuildIn", data: { prop: "hasOwnProperty" } }]
+        }
+    ]
 });