Repository: eslint/eslint
Pull request #14903 (2021-08-08 00:32:21)

Fix: no-useless-computed-key edge cases with class fields (refs #14857)
<!--
    Thank you for contributing!

    ESLint adheres to the [JS Foundation Code of Conduct](https://js.foundation/community/code-of-conduct).
-->

#### Prerequisites checklist

- [x] I have read the [contributing guidelines](https://github.com/eslint/eslint/blob/master/CONTRIBUTING.md).

#### What is the purpose of this pull request? (put an "X" next to an item)

<!--
    The following template is intentionally not a markdown checkbox list for the reasons
    explained in https://github.com/eslint/eslint/pull/12848#issuecomment-580302888
-->

[ ] Documentation update
[x] Bug fix ([template](https://raw.githubusercontent.com/eslint/eslint/master/templates/bug-report.md))
[ ] New rule ([template](https://raw.githubusercontent.com/eslint/eslint/master/templates/rule-proposal.md))
[ ] Changes an existing rule ([template](https://raw.githubusercontent.com/eslint/eslint/master/templates/rule-change-proposal.md))
[ ] Add autofixing to a rule
[ ] Add a CLI option
[ ] Add something to the core
[ ] Other, please explain:

<!--
    If the item you've checked above has a template, please paste the template questions below and answer them. (If this pull request is addressing an issue, you can just paste a link to the issue here instead.)
-->

refs #14857, fixes https://github.com/eslint/eslint/pull/14591#pullrequestreview-690053478

<!--
    Please ensure your pull request is ready:

    - Read the pull request guide (https://eslint.org/docs/developer-guide/contributing/pull-requests)
    - Include tests for this change
    - Update documentation for this change (if appropriate)
-->

<!--
    The following is required for all pull requests:
-->

#### What changes did you make? (Give an overview)

* Fixed the `no-useless-computed-key` rule to account for specific names like `"constructor"` in class fields.
* Updated the docs with class fields.


#### Is there anything you'd like reviewers to focus on?



Patch:
diff --git a/Makefile.js b/Makefile.js
index b93697857..3ee1b4c1b 100644
--- a/Makefile.js
+++ b/Makefile.js
@@ -43,7 +43,7 @@ const { cat, cd, cp, echo, exec, exit, find, ls, mkdir, pwd, rm, test } = requir
 const PERF_MULTIPLIER = 13e6;
 
 const OPEN_SOURCE_LICENSES = [
-    /MIT/u, /BSD/u, /Apache/u, /ISC/u, /WTF/u, /Public Domain/u, /LGPL/u
+    /MIT/u, /BSD/u, /Apache/u, /ISC/u, /WTF/u, /Public Domain/u, /LGPL/u, /Python/u
 ];
 
 //------------------------------------------------------------------------------
@@ -214,7 +214,7 @@ function generateRuleIndexPage() {
     // `.rules` will be `undefined` if all rules in category are deprecated.
     categoriesData.categories = categoriesData.categories.filter(category => !!category.rules);
 
-    const output = yaml.safeDump(categoriesData, { sortKeys: true });
+    const output = yaml.dump(categoriesData, { sortKeys: true });
 
     output.to(outputFile);
 }
@@ -390,7 +390,7 @@ function getFirstVersionOfDeletion(filePath) {
  * @private
  */
 function lintMarkdown(files) {
-    const config = yaml.safeLoad(fs.readFileSync(path.join(__dirname, "./.markdownlint.yml"), "utf8")),
+    const config = yaml.load(fs.readFileSync(path.join(__dirname, "./.markdownlint.yml"), "utf8")),
         result = markdownlint.sync({
             files,
             config,
diff --git a/README.md b/README.md
index a429bdf84..3082aeffb 100644
--- a/README.md
+++ b/README.md
@@ -298,7 +298,7 @@ The following companies, organizations, and individuals support ESLint's ongoing
 <p><a href="https://automattic.com"><img src="https://images.opencollective.com/photomatt/d0ef3e1/logo.png" alt="Automattic" height="undefined"></a></p><h3>Gold Sponsors</h3>
 <p><a href="https://nx.dev"><img src="https://images.opencollective.com/nx/0efbe42/logo.png" alt="Nx (by Nrwl)" height="96"></a> <a href="https://google.com/chrome"><img src="https://images.opencollective.com/chrome/dc55bd4/logo.png" alt="Chrome's Web Framework & Tools Performance Fund" height="96"></a> <a href="https://www.salesforce.com"><img src="https://images.opencollective.com/salesforce/ca8f997/logo.png" alt="Salesforce" height="96"></a> <a href="https://www.airbnb.com/"><img src="https://images.opencollective.com/airbnb/d327d66/logo.png" alt="Airbnb" height="96"></a> <a href="https://coinbase.com"><img src="https://avatars.githubusercontent.com/u/1885080?v=4" alt="Coinbase" height="96"></a> <a href="https://substack.com/"><img src="https://avatars.githubusercontent.com/u/53023767?v=4" alt="Substack" height="96"></a></p><h3>Silver Sponsors</h3>
 <p><a href="https://retool.com/"><img src="https://images.opencollective.com/retool/98ea68e/logo.png" alt="Retool" height="64"></a> <a href="https://liftoff.io/"><img src="https://images.opencollective.com/liftoff/5c4fa84/logo.png" alt="Liftoff" height="64"></a></p><h3>Bronze Sponsors</h3>
-<p><a href="https://mobilen.nu"><img src="https://images.opencollective.com/mobilen/e19860d/logo.png" alt="Mobilen" height="32"></a> <a href="https://www.crosswordsolver.org/anagram-solver/"><img src="https://images.opencollective.com/anagram-solver/2666271/logo.png" alt="Anagram Solver" height="32"></a> <a href="null"><img src="https://images.opencollective.com/bugsnag-stability-monitoring/c2cef36/logo.png" alt="Bugsnag Stability Monitoring" height="32"></a> <a href="https://mixpanel.com"><img src="https://images.opencollective.com/mixpanel/cd682f7/logo.png" alt="Mixpanel" height="32"></a> <a href="https://www.vpsserver.com"><img src="https://images.opencollective.com/vpsservercom/logo.png" alt="VPS Server" height="32"></a> <a href="https://icons8.com"><img src="https://images.opencollective.com/icons8/7fa1641/logo.png" alt="Icons8: free icons, photos, illustrations, and music" height="32"></a> <a href="https://discord.com"><img src="https://images.opencollective.com/discordapp/f9645d9/logo.png" alt="Discord" height="32"></a> <a href="https://themeisle.com"><img src="https://images.opencollective.com/themeisle/d5592fe/logo.png" alt="ThemeIsle" height="32"></a> <a href="https://www.firesticktricks.com"><img src="https://images.opencollective.com/fire-stick-tricks/b8fbe2c/logo.png" alt="Fire Stick Tricks" height="32"></a> <a href="https://www.practiceignition.com"><img src="https://avatars.githubusercontent.com/u/5753491?v=4" alt="Practice Ignition" height="32"></a></p>
+<p><a href="https://troypoint.com"><img src="https://images.opencollective.com/troypoint/080f96f/avatar.png" alt="TROYPOINT" height="32"></a> <a href="https://mobilen.nu"><img src="https://images.opencollective.com/mobilen/e19860d/logo.png" alt="Mobilen" height="32"></a> <a href="https://www.crosswordsolver.org/anagram-solver/"><img src="https://images.opencollective.com/anagram-solver/2666271/logo.png" alt="Anagram Solver" height="32"></a> <a href="null"><img src="https://images.opencollective.com/bugsnag-stability-monitoring/c2cef36/logo.png" alt="Bugsnag Stability Monitoring" height="32"></a> <a href="https://mixpanel.com"><img src="https://images.opencollective.com/mixpanel/cd682f7/logo.png" alt="Mixpanel" height="32"></a> <a href="https://www.vpsserver.com"><img src="https://images.opencollective.com/vpsservercom/logo.png" alt="VPS Server" height="32"></a> <a href="https://icons8.com"><img src="https://images.opencollective.com/icons8/7fa1641/logo.png" alt="Icons8: free icons, photos, illustrations, and music" height="32"></a> <a href="https://discord.com"><img src="https://images.opencollective.com/discordapp/f9645d9/logo.png" alt="Discord" height="32"></a> <a href="https://themeisle.com"><img src="https://images.opencollective.com/themeisle/d5592fe/logo.png" alt="ThemeIsle" height="32"></a> <a href="https://www.firesticktricks.com"><img src="https://images.opencollective.com/fire-stick-tricks/b8fbe2c/logo.png" alt="Fire Stick Tricks" height="32"></a> <a href="https://www.practiceignition.com"><img src="https://avatars.githubusercontent.com/u/5753491?v=4" alt="Practice Ignition" height="32"></a></p>
 <!--sponsorsend-->
 
 ## <a name="technology-sponsors"></a>Technology Sponsors
diff --git a/docs/rules/dot-notation.md b/docs/rules/dot-notation.md
index a13049a3f..26c77756c 100644
--- a/docs/rules/dot-notation.md
+++ b/docs/rules/dot-notation.md
@@ -46,6 +46,19 @@ var foo = { "class": "CS 101" }
 var x = foo["class"]; // Property name is a reserved word, square-bracket notation required
 ```
 
+Examples of additional **correct** code for the `{ "allowKeywords": false }` option:
+
+```js
+/*eslint dot-notation: ["error", { "allowKeywords": false }]*/
+
+class C {
+    #in;
+    foo() {
+        this.#in; // Dot notation is required for private identifiers
+    }
+}
+```
+
 ### allowPattern
 
 For example, when preparing data to be sent to an external API, it is often required to use property names that include underscores.  If the `camelcase` rule is in effect, these [snake case](https://en.wikipedia.org/wiki/Snake_case) properties would not be allowed.  By providing an `allowPattern` to the `dot-notation` rule, these snake case properties can be accessed with bracket notation.
diff --git a/docs/rules/func-names.md b/docs/rules/func-names.md
index 27b9cc20f..ea1ba617e 100644
--- a/docs/rules/func-names.md
+++ b/docs/rules/func-names.md
@@ -17,14 +17,14 @@ This rule can enforce or disallow the use of named function expressions.
 This rule has a string option:
 
 * `"always"` (default) requires function expressions to have a name
-* `"as-needed"` requires function expressions to have a name, if the name cannot be assigned automatically in an ES6 environment
+* `"as-needed"` requires function expressions to have a name, if the name isn't assigned automatically per the ECMAScript specification.
 * `"never"` disallows named function expressions, except in recursive functions, where a name is needed
 
 This rule has an object option:
 
 * `"generators": "always" | "as-needed" | "never"`
     * `"always"` require named generators
-    * `"as-needed"` require named generators if the name cannot be assigned automatically in an ES6 environment.
+    * `"as-needed"` require named generators if the name isn't assigned automatically per the ECMAScript specification.
     * `"never"` disallow named generators where possible.
 
 When a value for `generators` is not provided the behavior for generator functions falls back to the base option.
@@ -98,6 +98,13 @@ const cat = {
   meow: function() {}
 }
 
+class C {
+    #bar = function() {};
+    baz = function() {};
+}
+
+quux ??= function() {};
+
 (function bar() {
     // ...
 }())
diff --git a/docs/rules/no-console.md b/docs/rules/no-console.md
index f2fb79ec4..b1e4e601c 100644
--- a/docs/rules/no-console.md
+++ b/docs/rules/no-console.md
@@ -9,7 +9,7 @@ console.error("That shouldn't have happened.");
 
 ## Rule Details
 
-This rule disallows calls to methods of the `console` object.
+This rule disallows calls or assignments to methods of the `console` object.
 
 Examples of **incorrect** code for this rule:
 
@@ -19,6 +19,7 @@ Examples of **incorrect** code for this rule:
 console.log("Log a debug level message.");
 console.warn("Log a warn level message.");
 console.error("Log an error level message.");
+console.log = foo();
 ```
 
 Examples of **correct** code for this rule:
diff --git a/docs/rules/no-extra-parens.md b/docs/rules/no-extra-parens.md
index 68b986eff..b521dcbe2 100644
--- a/docs/rules/no-extra-parens.md
+++ b/docs/rules/no-extra-parens.md
@@ -48,6 +48,14 @@ for (a of (b));
 typeof (a);
 
 (function(){} ? a() : b());
+
+class A {
+    [(x)] = 1;
+}
+
+class B {
+    x = (y + z);
+}
 ```
 
 Examples of **correct** code for this rule with the default `"all"` option:
@@ -72,6 +80,14 @@ for (a of b);
 for (a in b, c);
 
 for (a in b);
+
+class A {
+    [x] = 1;
+}
+
+class B {
+    x = y + z;
+}
 ```
 
 ### conditionalAssign
diff --git a/docs/rules/no-multi-assign.md b/docs/rules/no-multi-assign.md
index 7ef705675..14721dab0 100644
--- a/docs/rules/no-multi-assign.md
+++ b/docs/rules/no-multi-assign.md
@@ -26,12 +26,19 @@ const foo = bar = "baz";
 let a =
     b =
     c;
+
+class Foo {
+    a = b = 10;
+}
+
+a = b = "quux";
 ```
 
 Examples of **correct** code for this rule:
 
 ```js
 /*eslint no-multi-assign: "error"*/
+
 var a = 5;
 var b = 5;
 var c = 5;
@@ -41,13 +48,21 @@ const bar = "baz";
 
 let a = c;
 let b = c;
+
+class Foo {
+    a = 10;
+    b = 10;
+}
+
+a = "quux";
+b = "quux";
 ```
 
 ## Options
 
 This rule has an object option:
 
-* `"ignoreNonDeclaration"`: When set to `true`, the rule allows chains that don't include initializing a variable in a declaration. Default is `false`.
+* `"ignoreNonDeclaration"`: When set to `true`, the rule allows chains that don't include initializing a variable in a declaration or initializing a class field. Default is `false`.
 
 ### ignoreNonDeclaration
 
@@ -73,6 +88,10 @@ Examples of **incorrect** code for the `{ "ignoreNonDeclaration": true }` option
 let a = b = "baz";
 
 const foo = bar = 1;
+
+class Foo {
+    a = b = 10;
+}
 ```
 
 ## Related Rules
diff --git a/docs/rules/no-useless-computed-key.md b/docs/rules/no-useless-computed-key.md
index 9b3c59284..2d3ddb3d5 100644
--- a/docs/rules/no-useless-computed-key.md
+++ b/docs/rules/no-useless-computed-key.md
@@ -20,7 +20,6 @@ Examples of **incorrect** code for this rule:
 
 ```js
 /*eslint no-useless-computed-key: "error"*/
-/*eslint-env es6*/
 
 var a = { ['0']: 0 };
 var a = { ['0+1,234']: 0 };
@@ -41,6 +40,18 @@ var c = { a: 0 };
 var c = { '0+1,234': 0 };
 ```
 
+Examples of additional **correct** code for this rule:
+
+```js
+/*eslint no-useless-computed-key: "error"*/
+
+var c = {
+    "__proto__": foo, // defines object's prototype
+
+    ["__proto__"]: bar // defines a property named "__proto__"
+};
+```
+
 ## Options
 
 This rule has an object option:
@@ -52,24 +63,64 @@ This rule has an object option:
 By default, this rule does not check class declarations and class expressions,
 as the default value for `enforceForClassMembers` is `false`.
 
-When `enforceForClassMembers` is set to `true`, the rule will also disallow unnecessary computed
-keys inside of class methods, getters and setters.
+When `enforceForClassMembers` is set to `true`, the rule will also disallow unnecessary computed keys inside of class fields, class methods, class getters, and class setters.
 
-Examples of **incorrect** code for `{ "enforceForClassMembers": true }`:
+Examples of **incorrect** code for this rule with the `{ "enforceForClassMembers": true }` option:
 
 ```js
 /*eslint no-useless-computed-key: ["error", { "enforceForClassMembers": true }]*/
 
 class Foo {
+    ["foo"] = "bar";
+
     [0]() {}
     ['a']() {}
     get ['b']() {}
     set ['c'](value) {}
 
+    static ["foo"] = "bar";
+
     static ['a']() {}
 }
 ```
 
+Examples of **correct** code for this rule with the `{ "enforceForClassMembers": true }` option:
+
+```js
+/*eslint no-useless-computed-key: ["error", { "enforceForClassMembers": true }]*/
+
+class Foo {
+    "foo" = "bar";
+
+    0() {}
+    'a'() {}
+    get 'b'() {}
+    set 'c'(value) {}
+
+    static "foo" = "bar";
+
+    static 'a'() {}
+}
+```
+
+Examples of additional **correct** code for this rule with the `{ "enforceForClassMembers": true }` option:
+
+```js
+/*eslint no-useless-computed-key: ["error", { "enforceForClassMembers": true }]*/
+
+class Foo {
+    ["constructor"]; // instance field named "constructor"
+
+    "constructor"() {} // the constructor of this class
+
+    ["constructor"]() {} // method named "constructor"
+
+    static ["constructor"]; // static field named "constructor"
+
+    static ["prototype"]; // runtime error, it would be a parsing error without `[]`
+}
+```
+
 ## When Not To Use It
 
 If you don't want to be notified about unnecessary computed property keys, you can safely disable this rule.
diff --git a/docs/rules/prefer-destructuring.md b/docs/rules/prefer-destructuring.md
index 7f18559ed..64e914fce 100644
--- a/docs/rules/prefer-destructuring.md
+++ b/docs/rules/prefer-destructuring.md
@@ -62,6 +62,17 @@ Examples of **correct** code when `enforceForRenamedProperties` is enabled:
 var { bar: foo } = object;
 ```
 
+Examples of additional **correct** code when `enforceForRenamedProperties` is enabled:
+
+```javascript
+class C {
+    #x;
+    foo() {
+        const bar = this.#x; // private identifiers are not allowed in destructuring
+    }
+}
+```
+
 An example configuration, with the defaults `array` and `object` filled in, looks like this:
 
 ```json
diff --git a/lib/cli-engine/formatters/tap.js b/lib/cli-engine/formatters/tap.js
index 354872a0c..e4148a3b3 100644
--- a/lib/cli-engine/formatters/tap.js
+++ b/lib/cli-engine/formatters/tap.js
@@ -31,7 +31,7 @@ function outputDiagnostics(diagnostic) {
     const prefix = "  ";
     let output = `${prefix}---\n`;
 
-    output += prefix + yaml.safeDump(diagnostic).split("\n").join(`\n${prefix}`);
+    output += prefix + yaml.dump(diagnostic).split("\n").join(`\n${prefix}`);
     output += "...\n";
     return output;
 }
diff --git a/lib/init/config-file.js b/lib/init/config-file.js
index 4c648ac05..a51f92e1c 100644
--- a/lib/init/config-file.js
+++ b/lib/init/config-file.js
@@ -63,7 +63,7 @@ function writeYAMLConfigFile(config, filePath) {
     // lazy load YAML to improve performance when not used
     const yaml = require("js-yaml");
 
-    const content = yaml.safeDump(config, { sortKeys: true });
+    const content = yaml.dump(config, { sortKeys: true });
 
     fs.writeFileSync(filePath, content, "utf8");
 }
diff --git a/lib/rules/dot-notation.js b/lib/rules/dot-notation.js
index 3aa9f3110..20434f359 100644
--- a/lib/rules/dot-notation.js
+++ b/lib/rules/dot-notation.js
@@ -141,6 +141,7 @@ module.exports = {
                 if (
                     !allowKeywords &&
                     !node.computed &&
+                    node.property.type === "Identifier" &&
                     keywords.indexOf(String(node.property.name)) !== -1
                 ) {
                     context.report({
diff --git a/lib/rules/no-extra-parens.js b/lib/rules/no-extra-parens.js
index 307e340c9..d06335046 100644
--- a/lib/rules/no-extra-parens.js
+++ b/lib/rules/no-extra-parens.js
@@ -808,13 +808,6 @@ module.exports = {
 
             CallExpression: checkCallNew,
 
-            ClassBody(node) {
-                node.body
-                    .filter(member => member.type === "MethodDefinition" && member.computed && member.key)
-                    .filter(member => hasExcessParensWithPrecedence(member.key, PRECEDENCE_OF_ASSIGNMENT_EXPR))
-                    .forEach(member => report(member.key));
-            },
-
             ConditionalExpression(node) {
                 if (isReturnAssignException(node)) {
                     return;
@@ -1063,6 +1056,12 @@ module.exports = {
                 }
             },
 
+            "MethodDefinition[computed=true]"(node) {
+                if (hasExcessParensWithPrecedence(node.key, PRECEDENCE_OF_ASSIGNMENT_EXPR)) {
+                    report(node.key);
+                }
+            },
+
             NewExpression: checkCallNew,
 
             ObjectExpression(node) {
@@ -1090,6 +1089,16 @@ module.exports = {
                 }
             },
 
+            PropertyDefinition(node) {
+                if (node.computed && hasExcessParensWithPrecedence(node.key, PRECEDENCE_OF_ASSIGNMENT_EXPR)) {
+                    report(node.key);
+                }
+
+                if (node.value && hasExcessParensWithPrecedence(node.value, PRECEDENCE_OF_ASSIGNMENT_EXPR)) {
+                    report(node.value);
+                }
+            },
+
             RestElement(node) {
                 const argument = node.argument;
 
diff --git a/lib/rules/no-useless-computed-key.js b/lib/rules/no-useless-computed-key.js
index 08df71265..a66430619 100644
--- a/lib/rules/no-useless-computed-key.js
+++ b/lib/rules/no-useless-computed-key.js
@@ -10,6 +10,76 @@
 
 const astUtils = require("./utils/ast-utils");
 
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
+/**
+ * Determines whether the computed key syntax is unnecessarily used for the given node.
+ * In particular, it determines whether removing the square brackets and using the content between them
+ * directly as the key (e.g. ['foo'] -> 'foo') would produce valid syntax and preserve the same behavior.
+ * Valid non-computed keys are only: identifiers, number literals and string literals.
+ * Only literals can preserve the same behavior, with a few exceptions for specific node types:
+ * Property
+ *   - { ["__proto__"]: foo } defines a property named "__proto__"
+ *     { "__proto__": foo } defines object's prototype
+ * PropertyDefinition
+ *   - class C { ["constructor"]; } defines an instance field named "constructor"
+ *     class C { "constructor"; } produces a parsing error
+ *   - class C { static ["constructor"]; } defines a static field named "constructor"
+ *     class C { static "constructor"; } produces a parsing error
+ *   - class C { static ["prototype"]; } produces a runtime error (doesn't break the whole script)
+ *     class C { static "prototype"; } produces a parsing error (breaks the whole script)
+ * MethodDefinition
+ *   - class C { ["constructor"]() {} } defines a prototype method named "constructor"
+ *     class C { "constructor"() {} } defines the constructor
+ *   - class C { static ["prototype"]() {} } produces a runtime error (doesn't break the whole script)
+ *     class C { static "prototype"() {} } produces a parsing error (breaks the whole script)
+ * @param {ASTNode} node The node to check. It can be `Property`, `PropertyDefinition` or `MethodDefinition`.
+ * @returns {void} `true` if the node has useless computed key.
+ */
+function hasUselessComputedKey(node) {
+    if (!node.computed) {
+        return false;
+    }
+
+    const { key } = node;
+
+    if (key.type !== "Literal") {
+        return false;
+    }
+
+    const { value } = key;
+
+    if (typeof value !== "number" && typeof value !== "string") {
+        return false;
+    }
+
+    switch (node.type) {
+        case "Property":
+            return value !== "__proto__";
+
+        case "PropertyDefinition":
+            if (node.static) {
+                return value !== "constructor" && value !== "prototype";
+            }
+
+            return value !== "constructor";
+
+        case "MethodDefinition":
+            if (node.static) {
+                return value !== "prototype";
+            }
+
+            return value !== "constructor";
+
+        /* istanbul ignore next */
+        default:
+            throw new Error(`Unexpected node type: ${node.type}`);
+    }
+
+}
+
 //------------------------------------------------------------------------------
 // Rule Definition
 //------------------------------------------------------------------------------
@@ -51,22 +121,9 @@ module.exports = {
          * @returns {void}
          */
         function check(node) {
-            if (!node.computed) {
-                return;
-            }
-
-            const key = node.key,
-                nodeType = typeof key.value;
-
-            let allowedKey;
-
-            if (node.type === "MethodDefinition") {
-                allowedKey = node.static ? "prototype" : "constructor";
-            } else {
-                allowedKey = "__proto__";
-            }
+            if (hasUselessComputedKey(node)) {
+                const { key } = node;
 
-            if (key.type === "Literal" && (nodeType === "string" || nodeType === "number") && key.value !== allowedKey) {
                 context.report({
                     node,
                     messageId: "unnecessarilyComputedProperty",
diff --git a/lib/rules/prefer-destructuring.js b/lib/rules/prefer-destructuring.js
index f82bb75c1..f76087e52 100644
--- a/lib/rules/prefer-destructuring.js
+++ b/lib/rules/prefer-destructuring.js
@@ -221,7 +221,11 @@ module.exports = {
          * @returns {void}
          */
         function performCheck(leftNode, rightNode, reportNode) {
-            if (rightNode.type !== "MemberExpression" || rightNode.object.type === "Super") {
+            if (
+                rightNode.type !== "MemberExpression" ||
+                rightNode.object.type === "Super" ||
+                rightNode.property.type === "PrivateIdentifier"
+            ) {
                 return;
             }
 
diff --git a/package.json b/package.json
index f0cd1bf67..746d6cc70 100644
--- a/package.json
+++ b/package.json
@@ -72,7 +72,7 @@
     "import-fresh": "^3.0.0",
     "imurmurhash": "^0.1.4",
     "is-glob": "^4.0.0",
-    "js-yaml": "^3.13.1",
+    "js-yaml": "^4.1.0",
     "json-stable-stringify-without-jsonify": "^1.0.1",
     "levn": "^0.4.1",
     "lodash.merge": "^4.6.2",


Test patch:
diff --git a/tests/lib/init/config-file.js b/tests/lib/init/config-file.js
index e9fe62e2c..50a9af1d5 100644
--- a/tests/lib/init/config-file.js
+++ b/tests/lib/init/config-file.js
@@ -60,8 +60,8 @@ describe("ConfigFile", () => {
         [
             ["JavaScript", "foo.js", espree.parse],
             ["JSON", "bar.json", JSON.parse],
-            ["YAML", "foo.yaml", yaml.safeLoad],
-            ["YML", "foo.yml", yaml.safeLoad]
+            ["YAML", "foo.yaml", yaml.load],
+            ["YML", "foo.yml", yaml.load]
         ].forEach(([fileType, filename, validate]) => {
 
             it(`should write a file through fs when a ${fileType} path is passed`, () => {
diff --git a/tests/lib/rules/dot-notation.js b/tests/lib/rules/dot-notation.js
index fed94c128..249faf80e 100644
--- a/tests/lib/rules/dot-notation.js
+++ b/tests/lib/rules/dot-notation.js
@@ -59,7 +59,12 @@ ruleTester.run("dot-notation", rule, {
         "a[void 0];",
         "a[b()];",
         { code: "a[/(?<zero>0)/];", parserOptions: { ecmaVersion: 2018 } },
-        { code: "class C { foo() { this['#a'] } }", parserOptions: { ecmaVersion: 2022 } }
+        { code: "class C { foo() { this['#a'] } }", parserOptions: { ecmaVersion: 2022 } },
+        {
+            code: "class C { #in; foo() { this.#in; } }",
+            options: [{ allowKeywords: false }],
+            parserOptions: { ecmaVersion: 2022 }
+        }
     ],
     invalid: [
         {
diff --git a/tests/lib/rules/no-extra-parens.js b/tests/lib/rules/no-extra-parens.js
index a14ececc0..5a4dba648 100644
--- a/tests/lib/rules/no-extra-parens.js
+++ b/tests/lib/rules/no-extra-parens.js
@@ -44,7 +44,7 @@ function invalid(code, output, type, line, config) {
 
 const ruleTester = new RuleTester({
     parserOptions: {
-        ecmaVersion: 2021,
+        ecmaVersion: 2022,
         ecmaFeatures: {
             jsx: true
         }
@@ -182,6 +182,25 @@ ruleTester.run("no-extra-parens", rule, {
         "class foo { a(){} [b](){} c(){} [(d,e)](){} }",
         "class foo { [(a,b)](){} c(){} [d](){} e(){} }",
         "const foo = class { constructor(){} a(){} get b(){} set b(bar){} get c(){} set d(baz){} static e(){} }",
+        "class foo { x; }",
+        "class foo { static x; }",
+        "class foo { x = 1; }",
+        "class foo { static x = 1; }",
+        "class foo { #x; }",
+        "class foo { static #x; }",
+        "class foo { static #x = 1; }",
+        "class foo { #x(){} get #y() {} set #y(value) {} static #z(){} static get #q() {} static set #q(value) {} }",
+        "const foo  = class { #x(){} get #y() {} set #y(value) {} static #z(){} static get #q() {} static set #q(value) {} }",
+        "class foo { [(x, y)]; }",
+        "class foo { static [(x, y)]; }",
+        "class foo { [(x, y)] = 1; }",
+        "class foo { static [(x, y)] = 1; }",
+        "class foo { x = (y, z); }",
+        "class foo { static x = (y, z); }",
+        "class foo { #x = (y, z); }",
+        "class foo { static #x = (y, z); }",
+        "class foo { [(1, 2)] = (3, 4) }",
+        "const foo = class { [(1, 2)] = (3, 4) }",
 
         // ExpressionStatement restricted productions
         "({});",
@@ -771,6 +790,26 @@ ruleTester.run("no-extra-parens", rule, {
         invalid("class foo { [(a,b)](){} [(c+d)](){} }", "class foo { [(a,b)](){} [c+d](){} }", "BinaryExpression"),
         invalid("class foo { [a+(b*c)](){} }", "class foo { [a+b*c](){} }", "BinaryExpression"),
         invalid("const foo = class { [(a)](){} }", "const foo = class { [a](){} }", "Identifier"),
+        invalid("class foo { [(x)]; }", "class foo { [x]; }", "Identifier"),
+        invalid("class foo { static [(x)]; }", "class foo { static [x]; }", "Identifier"),
+        invalid("class foo { [(x)] = 1; }", "class foo { [x] = 1; }", "Identifier"),
+        invalid("class foo { static [(x)] = 1; }", "class foo { static [x] = 1; }", "Identifier"),
+        invalid("const foo = class { [(x)]; }", "const foo = class { [x]; }", "Identifier"),
+        invalid("class foo { [(x = y)]; }", "class foo { [x = y]; }", "AssignmentExpression"),
+        invalid("class foo { [(x + y)]; }", "class foo { [x + y]; }", "BinaryExpression"),
+        invalid("class foo { [(x ? y : z)]; }", "class foo { [x ? y : z]; }", "ConditionalExpression"),
+        invalid("class foo { [((x, y))]; }", "class foo { [(x, y)]; }", "SequenceExpression"),
+        invalid("class foo { x = (y); }", "class foo { x = y; }", "Identifier"),
+        invalid("class foo { static x = (y); }", "class foo { static x = y; }", "Identifier"),
+        invalid("class foo { #x = (y); }", "class foo { #x = y; }", "Identifier"),
+        invalid("class foo { static #x = (y); }", "class foo { static #x = y; }", "Identifier"),
+        invalid("const foo = class { x = (y); }", "const foo = class { x = y; }", "Identifier"),
+        invalid("class foo { x = (() => {}); }", "class foo { x = () => {}; }", "ArrowFunctionExpression"),
+        invalid("class foo { x = (y + z); }", "class foo { x = y + z; }", "BinaryExpression"),
+        invalid("class foo { x = (y ? z : q); }", "class foo { x = y ? z : q; }", "ConditionalExpression"),
+        invalid("class foo { x = ((y, z)); }", "class foo { x = (y, z); }", "SequenceExpression"),
+
+        //
         invalid(
             "var foo = (function*() { if ((yield foo())) { return; } }())",
             "var foo = (function*() { if (yield foo()) { return; } }())",
diff --git a/tests/lib/rules/no-useless-computed-key.js b/tests/lib/rules/no-useless-computed-key.js
index a7bf323cf..37f311188 100644
--- a/tests/lib/rules/no-useless-computed-key.js
+++ b/tests/lib/rules/no-useless-computed-key.js
@@ -42,6 +42,9 @@ ruleTester.run("no-useless-computed-key", rule, {
         { code: "class Foo { static ['constructor']() {} }", options: [{ enforceForClassMembers: false }] },
         { code: "class Foo { ['prototype']() {} }", options: [{ enforceForClassMembers: false }] },
         { code: "class Foo { a }", options: [{ enforceForClassMembers: true }] },
+        { code: "class Foo { ['constructor'] }", options: [{ enforceForClassMembers: true }] },
+        { code: "class Foo { static ['constructor'] }", options: [{ enforceForClassMembers: true }] },
+        { code: "class Foo { static ['prototype'] }", options: [{ enforceForClassMembers: true }] },
 
         /*
          * Well-known browsers throw syntax error bigint literals on property names,
@@ -241,6 +244,22 @@ ruleTester.run("no-useless-computed-key", rule, {
                 data: { property: "2" },
                 type: "Property"
             }]
+        }, {
+            code: "({ ['constructor']: 1 })",
+            output: "({ 'constructor': 1 })",
+            errors: [{
+                messageId: "unnecessarilyComputedProperty",
+                data: { property: "'constructor'" },
+                type: "Property"
+            }]
+        }, {
+            code: "({ ['prototype']: 1 })",
+            output: "({ 'prototype': 1 })",
+            errors: [{
+                messageId: "unnecessarilyComputedProperty",
+                data: { property: "'prototype'" },
+                type: "Property"
+            }]
         }, {
             code: "class Foo { ['0']() {} }",
             output: "class Foo { '0'() {} }",
@@ -461,6 +480,24 @@ ruleTester.run("no-useless-computed-key", rule, {
                 data: { property: "'x'" },
                 type: "MethodDefinition"
             }]
+        }, {
+            code: "(class { ['__proto__']() {} })",
+            output: "(class { '__proto__'() {} })",
+            options: [{ enforceForClassMembers: true }],
+            errors: [{
+                messageId: "unnecessarilyComputedProperty",
+                data: { property: "'__proto__'" },
+                type: "MethodDefinition"
+            }]
+        }, {
+            code: "(class { static ['__proto__']() {} })",
+            output: "(class { static '__proto__'() {} })",
+            options: [{ enforceForClassMembers: true }],
+            errors: [{
+                messageId: "unnecessarilyComputedProperty",
+                data: { property: "'__proto__'" },
+                type: "MethodDefinition"
+            }]
         }, {
             code: "(class { static ['constructor']() {} })",
             output: "(class { static 'constructor'() {} })",
@@ -515,6 +552,33 @@ ruleTester.run("no-useless-computed-key", rule, {
                 data: { property: "'#foo'" },
                 type: "PropertyDefinition"
             }]
+        }, {
+            code: "(class { ['__proto__'] })",
+            output: "(class { '__proto__' })",
+            options: [{ enforceForClassMembers: true }],
+            errors: [{
+                messageId: "unnecessarilyComputedProperty",
+                data: { property: "'__proto__'" },
+                type: "PropertyDefinition"
+            }]
+        }, {
+            code: "(class { static ['__proto__'] })",
+            output: "(class { static '__proto__' })",
+            options: [{ enforceForClassMembers: true }],
+            errors: [{
+                messageId: "unnecessarilyComputedProperty",
+                data: { property: "'__proto__'" },
+                type: "PropertyDefinition"
+            }]
+        }, {
+            code: "(class { ['prototype'] })",
+            output: "(class { 'prototype' })",
+            options: [{ enforceForClassMembers: true }],
+            errors: [{
+                messageId: "unnecessarilyComputedProperty",
+                data: { property: "'prototype'" },
+                type: "PropertyDefinition"
+            }]
         }
     ]
 });
diff --git a/tests/lib/rules/prefer-destructuring.js b/tests/lib/rules/prefer-destructuring.js
index 8f111a1d8..7f808a642 100644
--- a/tests/lib/rules/prefer-destructuring.js
+++ b/tests/lib/rules/prefer-destructuring.js
@@ -16,7 +16,7 @@ const rule = require("../../../lib/rules/prefer-destructuring"),
 // Tests
 //------------------------------------------------------------------------------
 
-const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 2020 } });
+const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 2022 } });
 
 ruleTester.run("prefer-destructuring", rule, {
     valid: [
@@ -157,7 +157,40 @@ ruleTester.run("prefer-destructuring", rule, {
 
         // Optional chaining
         "var foo = array?.[0];", // because the fixed code can throw TypeError.
-        "var foo = object?.foo;"
+        "var foo = object?.foo;",
+
+        // Private identifiers
+        "class C { #x; foo() { const x = this.#x; } }",
+        "class C { #x; foo() { x = this.#x; } }",
+        "class C { #x; foo(a) { x = a.#x; } }",
+        {
+            code: "class C { #x; foo() { const x = this.#x; } }",
+            options: [{ array: true, object: true }, { enforceForRenamedProperties: true }]
+        },
+        {
+            code: "class C { #x; foo() { const y = this.#x; } }",
+            options: [{ array: true, object: true }, { enforceForRenamedProperties: true }]
+        },
+        {
+            code: "class C { #x; foo() { x = this.#x; } }",
+            options: [{ array: true, object: true }, { enforceForRenamedProperties: true }]
+        },
+        {
+            code: "class C { #x; foo() { y = this.#x; } }",
+            options: [{ array: true, object: true }, { enforceForRenamedProperties: true }]
+        },
+        {
+            code: "class C { #x; foo(a) { x = a.#x; } }",
+            options: [{ array: true, object: true }, { enforceForRenamedProperties: true }]
+        },
+        {
+            code: "class C { #x; foo(a) { y = a.#x; } }",
+            options: [{ array: true, object: true }, { enforceForRenamedProperties: true }]
+        },
+        {
+            code: "class C { #x; foo() { x = this.a.#x; } }",
+            options: [{ array: true, object: true }, { enforceForRenamedProperties: true }]
+        }
     ],
 
     invalid: [