Repository: eslint/eslint (js)

Issue:
no-console warns on assignment
<!--
    ESLint adheres to the Open JS Foundation Code of Conduct:
    https://eslint.org/conduct

    This template is for bug reports. If you are here for another reason, please see below:

    1. To propose a new rule: https://eslint.org/docs/developer-guide/contributing/new-rules
    2. To request a rule change: https://eslint.org/docs/developer-guide/contributing/rule-changes
    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://eslint.org/chat/help

    Note that leaving sections blank will make it difficult for us to troubleshoot and we may have to close the issue.
-->


**Tell us about your environment**

<!--
    If you are using ESLint v6.5.0 or later, you can run ESLint with the `--env-info` flag and paste the output here.
-->

* **ESLint Version:** v7.30.0
* **Node Version:** v16.4.0
* **npm Version:** v7.18.1
* **Operating System:** linux 5.8.0-58-generic

**What parser (default, `@babel/eslint-parser`, `@typescript-eslint/parser`, etc.) are you using?**
`@typescript-eslint/parser`
<!-- if you are using a parser other than ESLint's default, please try using the default parser to verify if the issue is parser-specific -->

**Please show your full configuration:**

<details>
<summary>Configuration</summary>

<!-- Paste your configuration below: -->
```js
module.exports = {
  parserOptions: {
      ecmaVersion: 6
  },
  rules: {
    "no-console": "warn",
  },
}
```

</details>

**What did you do? Please include the actual source code causing the issue, as well as the command that you used to run ESLint.**

<!-- Paste the source code below: -->
```js
console.log = jest.fn()
```

<!-- Paste the command you used to run ESLint: -->
```bash
npx eslint .
```

**What did you expect to happen?**
The `no-console` rule should detect that this is an assignment and not a call. In [the rule details](https://eslint.org/docs/rules/no-console#rule-details) it says "This rule disallows calls to methods of the console object."

**What actually happened? Please copy-paste the actual, raw output from ESLint.**
`warning  Unexpected console statement  no-console`

**Steps to reproduce this issue:**

<!-- Please tell us exactly how to see the issue you're describing -->

1. in your eslint configuration add `no-console: 'warn'` to your `rules`.
2. in your code, set `console.log = null`

**Are you willing to submit a pull request to fix this bug?**
yes

Pull request:
Docs: improve rule details for `no-console` (fixes #14793)
<!--
    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
-->

[X] Documentation update


<!--
    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.)
-->

<!--
    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)

Fix #14793

Update rule details to clarify this rule disallows assignments as well.

#### Is there anything you'd like reviewers to focus on?
No

Patch:
diff --git a/.markdownlintignore b/.markdownlintignore
new file mode 100644
index 000000000..1b763b1ba
--- /dev/null
+++ b/.markdownlintignore
@@ -0,0 +1,1 @@
+CHANGELOG.md
diff --git a/Makefile.js b/Makefile.js
index e84d21abf..b93697857 100644
--- a/Makefile.js
+++ b/Makefile.js
@@ -66,7 +66,8 @@ const NODE = "node ", // intentional extra space
     // Files
     RULE_FILES = glob.sync("lib/rules/*.js").filter(filePath => path.basename(filePath) !== "index.js"),
     JSON_FILES = find("conf/").filter(fileType("json")),
-    MARKDOWN_FILES_ARRAY = find("docs/").concat(ls(".")).filter(fileType("md")),
+    MARKDOWNLINT_IGNORED_FILES = fs.readFileSync(path.join(__dirname, ".markdownlintignore"), "utf-8").split("\n"),
+    MARKDOWN_FILES_ARRAY = find("docs/").concat(ls(".")).filter(fileType("md")).filter(file => !MARKDOWNLINT_IGNORED_FILES.includes(file)),
     TEST_FILES = "\"tests/{bin,lib,tools}/**/*.js\"",
     PERF_ESLINTRC = path.join(PERF_TMP_DIR, "eslintrc.yml"),
     PERF_MULTIFILES_TARGET_DIR = path.join(PERF_TMP_DIR, "eslint"),
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/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/package.json b/package.json
index 98b79a48f..f0cd1bf67 100644
--- a/package.json
+++ b/package.json
@@ -59,7 +59,7 @@
     "escape-string-regexp": "^4.0.0",
     "eslint-scope": "^6.0.0",
     "eslint-utils": "^3.0.0",
-    "eslint-visitor-keys": "^2.1.0",
+    "eslint-visitor-keys": "^3.0.0",
     "espree": "^8.0.0",
     "esquery": "^1.4.0",
     "esutils": "^2.0.2",
@@ -116,8 +116,8 @@
     "karma-webpack": "^5.0.0",
     "lint-staged": "^11.0.0",
     "load-perf": "^0.2.0",
-    "markdownlint": "^0.19.0",
-    "markdownlint-cli": "^0.22.0",
+    "markdownlint": "^0.23.1",
+    "markdownlint-cli": "^0.28.1",
     "memfs": "^3.0.1",
     "mocha": "^8.3.2",
     "mocha-junit-reporter": "^2.0.0",


Test patch:
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; } }())",