Repository: eslint/eslint (js)

Issue:
camelcase rule doesn't ignore default parameter assignment when disabled
**Tell us about your environment**

**ESLint Version:** 3.19.0
**Node Version:** 6.9.4
**npm Version:** 3.10.10

**What parser (default, Babel-ESLint, etc.) are you using?** Default

**Please show your full configuration:**

```
module.exports = {
    "extends": "standard",
    "plugins": [
        "standard",
        "promise"
    ],
    "parserOptions": {
      "ecmaVersion": 6
    },
    "env": {
      "node": true
    },
    "rules": {
      "semi": ["error", "always"],
      "camelcase": [2, {"properties": "never"}]
    }
};
```

**What did you do? Please include the actual source code causing the issue.**

Disabling the `camelcase` rule does not appear to respect ES6 default parameter assignment when used with object destructuring syntax.

Here's a simple example:

```
// example.js

function snakeCase ({
  param_1 = 'default value',
  param_2 = 'another default'
} = {}) {
  // do stuff
}

module.exports = snakeCase;

```

**What did you expect to happen?**

Eslint should not care that `param_1` and `param_2` are using snake case instead of camelCase as the rule is disabled

**What actually happened? Please include the actual, raw output from ESLint.**

```
error  Identifier 'param_1' is not in camel case                            camelcase
error  Identifier 'param_2' is not in camel case                            camelcase
```

Pull request description:
Update: update handling of destructuring in camelcase (fixes #8511)
<!--
    ESLint adheres to the [JS Foundation Code of Conduct](https://js.foundation/community/code-of-conduct).
-->

**What is the purpose of this pull request? (put an "X" next to item)**

[ ] 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:

What changes did you make? (Give an overview)
Fixes the bug reported in https://github.com/eslint/eslint/issues/8511 . I forked from https://github.com/eslint/eslint/pull/8529 and added a check for the right-side of the value assignment

Is there anything you'd like reviewers to focus on?
Nope!



Patch:
diff --git a/docs/rules/camelcase.md b/docs/rules/camelcase.md
index 66abcccf8..db660bcee 100644
--- a/docs/rules/camelcase.md
+++ b/docs/rules/camelcase.md
@@ -32,9 +32,27 @@ obj.do_something = function() {
     // ...
 };
 
+function foo({ no_camelcased }) {
+    // ...
+};
+
+function foo({ isCamelcased: no_camelcased }) {
+    // ...
+}
+
+function foo({ no_camelcased = 'default value' }) {
+    // ...
+};
+
 var obj = {
     my_pref: 1
 };
+
+var { category_id = 1 } = query;
+
+var { foo: no_camelcased } = bar;
+
+var { foo: bar_baz = 1 } = quz;
 ```
 
 Examples of **correct** code for this rule with the default `{ "properties": "always" }` option:
@@ -56,6 +74,25 @@ do_something();
 new do_something();
 
 var { category_id: category } = query;
+
+function foo({ isCamelCased }) {
+    // ...
+};
+
+function foo({ isCamelCased: isAlsoCamelCased }) {
+    // ...
+}
+
+function foo({ isCamelCased = 'default value' }) {
+    // ...
+};
+
+var { categoryId = 1 } = query;
+
+var { foo: isCamelCased } = bar;
+
+var { foo: isCamelCased = 1 } = quz;
+
 ```
 
 ### never
diff --git a/docs/rules/max-len.md b/docs/rules/max-len.md
index 55a41fcfa..71339f01c 100644
--- a/docs/rules/max-len.md
+++ b/docs/rules/max-len.md
@@ -30,7 +30,7 @@ This rule has a number or object option:
 Examples of **incorrect** code for this rule with the default `{ "code": 80 }` option:
 
 ```js
-/*eslint max-len: ["error", 80]*/
+/*eslint max-len: ["error", { "code": 80 }]*/
 
 var foo = { "bar": "This is a bar.", "baz": { "qux": "This is a qux" }, "difficult": "to read" };
 ```
@@ -38,7 +38,7 @@ var foo = { "bar": "This is a bar.", "baz": { "qux": "This is a qux" }, "difficu
 Examples of **correct** code for this rule with the default `{ "code": 80 }` option:
 
 ```js
-/*eslint max-len: ["error", 80]*/
+/*eslint max-len: ["error", { "code": 80 }]*/
 
 var foo = {
   "bar": "This is a bar.",
@@ -52,7 +52,7 @@ var foo = {
 Examples of **incorrect** code for this rule with the default `{ "tabWidth": 4 }` option:
 
 ```js
-/*eslint max-len: ["error", 80, 4]*/
+/*eslint max-len: ["error", { "code": 80, "tabWidth": 4 }]*/
 
 \t  \t  var foo = { "bar": "This is a bar.", "baz": { "qux": "This is a qux" } };
 ```
@@ -60,7 +60,7 @@ Examples of **incorrect** code for this rule with the default `{ "tabWidth": 4 }
 Examples of **correct** code for this rule with the default `{ "tabWidth": 4 }` option:
 
 ```js
-/*eslint max-len: ["error", 80, 4]*/
+/*eslint max-len: ["error", { "code": 80, "tabWidth": 4 }]*/
 
 \t  \t  var foo = {
 \t  \t  \t  \t  "bar": "This is a bar.",
diff --git a/lib/rules/camelcase.js b/lib/rules/camelcase.js
index 6fb1475b2..aafa876f6 100644
--- a/lib/rules/camelcase.js
+++ b/lib/rules/camelcase.js
@@ -92,34 +92,45 @@ module.exports = {
                     }
 
                     // Always report underscored object names
-                    if (node.parent.object.type === "Identifier" &&
-                            node.parent.object.name === node.name &&
-                            isUnderscored(name)) {
+                    if (node.parent.object.type === "Identifier" && node.parent.object.name === node.name && isUnderscored(name)) {
                         report(node);
 
                     // Report AssignmentExpressions only if they are the left side of the assignment
-                    } else if (effectiveParent.type === "AssignmentExpression" &&
-                            isUnderscored(name) &&
-                            (effectiveParent.right.type !== "MemberExpression" ||
-                            effectiveParent.left.type === "MemberExpression" &&
-                            effectiveParent.left.property.name === node.name)) {
+                    } else if (effectiveParent.type === "AssignmentExpression" && isUnderscored(name) && (effectiveParent.right.type !== "MemberExpression" || effectiveParent.left.type === "MemberExpression" && effectiveParent.left.property.name === node.name)) {
                         report(node);
                     }
 
-                // Properties have their own rules
-                } else if (node.parent.type === "Property") {
+                /*
+                 * Properties have their own rules, and
+                 * AssignmentPattern nodes can be treated like Properties:
+                 * e.g.: const { no_camelcased = false } = bar;
+                 */
+                } else if (node.parent.type === "Property" || node.parent.type === "AssignmentPattern") {
 
-                    // "never" check properties
-                    if (properties === "never") {
-                        return;
+                    if (node.parent.parent && node.parent.parent.type === "ObjectPattern") {
+
+                        if (node.parent.shorthand && node.parent.value.left && isUnderscored(node.parent.value.left.name)) {
+
+                            report(node);
+                        }
+
+                        // prevent checking righthand side of destructured object
+                        if (node.parent.key === node && node.parent.value !== node) {
+                            return;
+                        }
+
+                        if (node.parent.value.name && isUnderscored(node.parent.value.name)) {
+                            report(node);
+                        }
                     }
 
-                    if (node.parent.parent && node.parent.parent.type === "ObjectPattern" &&
-                            node.parent.key === node && node.parent.value !== node) {
+                    // "never" check properties
+                    if (properties === "never") {
                         return;
                     }
 
-                    if (isUnderscored(name) && !ALLOWED_PARENT_TYPES.has(effectiveParent.type)) {
+                    // don't check right hand side of AssignmentExpression to prevent duplicate warnings
+                    if (isUnderscored(name) && !ALLOWED_PARENT_TYPES.has(effectiveParent.type) && !(node.parent.right === node)) {
                         report(node);
                     }
 


Test patch:
diff --git a/tests/lib/rules/camelcase.js b/tests/lib/rules/camelcase.js
index 5983fb1c6..12716b5db 100644
--- a/tests/lib/rules/camelcase.js
+++ b/tests/lib/rules/camelcase.js
@@ -63,11 +63,6 @@ ruleTester.run("camelcase", rule, {
             code: "var { category_id: category } = query;",
             parserOptions: { ecmaVersion: 6 }
         },
-        {
-            code: "var { category_id: category } = query;",
-            options: [{ properties: "never" }],
-            parserOptions: { ecmaVersion: 6 }
-        },
         {
             code: "import { camelCased } from \"external module\";",
             parserOptions: { ecmaVersion: 6, sourceType: "module" }
@@ -79,6 +74,18 @@ ruleTester.run("camelcase", rule, {
         {
             code: "import { no_camelcased as camelCased, anoterCamelCased } from \"external-module\";",
             parserOptions: { ecmaVersion: 6, sourceType: "module" }
+        },
+        {
+            code: "function foo({ no_camelcased: camelCased }) {};",
+            parserOptions: { ecmaVersion: 6 }
+        },
+        {
+            code: "function foo({ camelCased = 'default value' }) {};",
+            parserOptions: { ecmaVersion: 6 }
+        },
+        {
+            code: "function foo({ camelCased }) {};",
+            parserOptions: { ecmaVersion: 6 }
         }
     ],
     invalid: [
@@ -212,6 +219,16 @@ ruleTester.run("camelcase", rule, {
                 }
             ]
         },
+        {
+            code: "var { category_id = 1 } = query;",
+            parserOptions: { ecmaVersion: 6 },
+            errors: [
+                {
+                    message: "Identifier 'category_id' is not in camel case.",
+                    type: "Identifier"
+                }
+            ]
+        },
         {
             code: "import no_camelcased from \"external-module\";",
             parserOptions: { ecmaVersion: 6, sourceType: "module" },
@@ -301,6 +318,90 @@ ruleTester.run("camelcase", rule, {
                     type: "Identifier"
                 }
             ]
+        },
+        {
+            code: "function foo({ no_camelcased }) {};",
+            parserOptions: { ecmaVersion: 6 },
+            errors: [
+                {
+                    message: "Identifier 'no_camelcased' is not in camel case.",
+                    type: "Identifier"
+                }
+            ]
+        },
+        {
+            code: "function foo({ no_camelcased = 'default value' }) {};",
+            parserOptions: { ecmaVersion: 6 },
+            errors: [
+                {
+                    message: "Identifier 'no_camelcased' is not in camel case.",
+                    type: "Identifier"
+                }
+            ]
+        },
+        {
+            code: "const no_camelcased = 0; function foo({ camelcased_value = no_camelcased}) {}",
+            parserOptions: { ecmaVersion: 6 },
+            errors: [
+                {
+                    message: "Identifier 'no_camelcased' is not in camel case.",
+                    type: "Identifier"
+                },
+                {
+                    message: "Identifier 'camelcased_value' is not in camel case.",
+                    type: "Identifier"
+                }
+            ]
+        },
+        {
+            code: "const { bar: no_camelcased } = foo;",
+            parserOptions: { ecmaVersion: 6 },
+            errors: [
+                {
+                    message: "Identifier 'no_camelcased' is not in camel case.",
+                    type: "Identifier"
+                }
+            ]
+        },
+        {
+            code: "function foo({ value_1: my_default }) {}",
+            parserOptions: { ecmaVersion: 6 },
+            errors: [
+                {
+                    message: "Identifier 'my_default' is not in camel case.",
+                    type: "Identifier"
+                }
+            ]
+        },
+        {
+            code: "function foo({ isCamelcased: no_camelcased }) {};",
+            parserOptions: { ecmaVersion: 6 },
+            errors: [
+                {
+                    message: "Identifier 'no_camelcased' is not in camel case.",
+                    type: "Identifier"
+                }
+            ]
+        },
+        {
+            code: "var { foo: bar_baz = 1 } = quz;",
+            parserOptions: { ecmaVersion: 6 },
+            errors: [
+                {
+                    message: "Identifier 'bar_baz' is not in camel case.",
+                    type: "Identifier"
+                }
+            ]
+        },
+        {
+            code: "const { no_camelcased = false } = bar;",
+            parserOptions: { ecmaVersion: 6 },
+            errors: [
+                {
+                    message: "Identifier 'no_camelcased' is not in camel case.",
+                    type: "Identifier"
+                }
+            ]
         }
     ]
 });
diff --git a/tests/lib/testers/rule-tester.js b/tests/lib/testers/rule-tester.js
index 195a616d4..d7a4bd51c 100644
--- a/tests/lib/testers/rule-tester.js
+++ b/tests/lib/testers/rule-tester.js
@@ -126,7 +126,7 @@ describe("RuleTester", () => {
                     { code: "var foo = bar;", errors: [{ message: "Bad error message." }] }
                 ]
             });
-        }, /Bad var\..*==.*Bad error message/);
+        }, /Bad var\.((.*==)|(.*strictEqual)).*Bad error message/);
     });
 
     it("should throw an error when the error message regex does not match", () => {
@@ -167,7 +167,7 @@ describe("RuleTester", () => {
                     { code: "var foo = bar;", errors: ["Bad error message."] }
                 ]
             });
-        }, /Bad var\..*==.*Bad error message/);
+        }, /Bad var\.((.*==)|(.*strictEqual)).*Bad error message/);
     });
 
     it("should throw an error when the error is a string and it does not match error message", () => {