Repository: eslint/eslint
Pull request #10783 (2018-08-21 20:27:26)

Update: camelcase rule ignoreList added
**What is the purpose of this pull request? (put an "X" next to item)**

[ ] Documentation update
[ ] Bug fix
[ ] New rule
[X] Changes an existing rule
[ ] Add autofixing to a rule
[ ] Add a CLI option
[ ] Add something to the core
[ ] Other, please explain:

**What rule do you want to change?**

camelcase

**Does this change cause the rule to produce more or fewer warnings?**

No, only less if used.

**How will the change be implemented? (New option, new default behavior, etc.)?**

New option to the rule.

**Please provide some example code that this change will affect:**

```js
class MyComponent extends React.Component {
  UNSAFE_componentWillMount() {
    // ...
  }
}
```

**What does the rule currently do for this code?**

It throw an error: `Identifier 'UNSAFE_componentWillMount' is not in camel case.`

**What will the rule do after it's changed?**

Using the added option: `{ ignoreList: ["UNSAFE_componentWillMount"] }` will allow this method name.

**What changes did you make? (Give an overview)**

Camel case is used, more and more, but there is still many use of underscore, basically for:
 - The `_id` for MongoDB
 - So, many people still use underscore for identifiers, like: `user_id`
 - Also, some methods of some frameworks like React have methods like `UNSAFE_componentWillMount`
 - etc.

I have seen some discussions, it seems that people is trying to find the best way to do what this update add:
 - Ignoring all variables finishing with `_id`, with the option `{ ignoreList: ["_id$"] }`
 - Ignoring all methods starting with `UNSAFE_`, with the option `{ ignoreList: ["^UNSAFE_"] }`
 - Or add some of their specific variables: `{ ignoreList: ["_main_contex_"] }`
 
Basically, it allow to ignore specific variables, and/or regexp pattern, simply.

One of the discussions, for example: https://github.com/eslint/eslint/issues/9435
 
**Also**: I've seen this PR: https://github.com/eslint/eslint/issues/10503, after my work :( But I prefer mine, do not hesitate to close this one. My bad.

**Is there anything you'd like reviewers to focus on?**

Tests, I have added some tests for my rule, but no overkill tests basically cover some already covered scenarios.

platinumazure: Hi @Shudrum, thanks for the PR. I apologize that we let this sit so long.

I've given #10503 a nudge. Hoping that the team can evaluate that one in the next few days. If that issue is accepted, we can review and potentially merge this PR.

Thanks for your patience!
platinumazure: Hi @Shudrum, thanks for contributing and apologies for letting this sit so long.

It looks like a separate issue was created (#10503), where the ESLint team eventually accepted a slightly different proposal.

Would you be willing to make some changes to this PR to follow that proposal? Or, if you think the proposal we accepted does not cover the use cases you had envisioned in this PR, would you be willing to leave a comment and explain what might need to be changed?

If you're busy and no longer have time to work on this, that's okay too.

Thanks and sorry again for letting this slip through the cracks.
Shudrum: We are currently release our all new front end. If you can wait 2 / 3 days I'll see that :)
Shudrum: If I have understood:

The other PR allow a list of authorised variables:

```
"camelcase": ['error', {
  "exceptions": [
    "UNSAFE_componentDidMount",
    "UNSAFE_componentWillReceiveProps",
    "UNSAFE_componentWillUpdate"
  ]
}]
```

Mine do the same thing, but allow also regex:

```
"camelcase": ['error', {
  "ignoreList": [
    "UNSAFE_componentDidMount",
    "_id$",
    "^UNSAFE_"
  ]
}]
```

As asked by @sindresorhus: https://github.com/eslint/eslint/issues/10503#issuecomment-400170214

I think this is the best way to manage this option.

I will update my PR from the review, also, I will change `ignoreList` by `allow` as asked by @nzakas: https://github.com/eslint/eslint/issues/10503#issuecomment-422092067
Shudrum: PR updated.

Maybe I missed something, do not hesitate.

Patch:
diff --git a/.github/ISSUE_TEMPLATE/BUG_REPORT.md b/.github/ISSUE_TEMPLATE/BUG_REPORT.md
index ef8c5101c..47c0c84c3 100644
--- a/.github/ISSUE_TEMPLATE/BUG_REPORT.md
+++ b/.github/ISSUE_TEMPLATE/BUG_REPORT.md
@@ -1,5 +1,5 @@
 ---
-name: Bug report
+name: "\U0001F41E  Bug report"
 about: Report an issue with ESLint or rules bundled with ESLint
 
 ---
@@ -51,4 +51,4 @@ about: Report an issue with ESLint or rules bundled with ESLint
 
 **What did you expect to happen?**
 
-**What actually happened? Please include the actual, raw output from ESLint.**
\ No newline at end of file
+**What actually happened? Please include the actual, raw output from ESLint.**
diff --git a/.github/ISSUE_TEMPLATE/CHANGE.md b/.github/ISSUE_TEMPLATE/CHANGE.md
index 14cff91b0..b5ae2ae83 100644
--- a/.github/ISSUE_TEMPLATE/CHANGE.md
+++ b/.github/ISSUE_TEMPLATE/CHANGE.md
@@ -1,5 +1,5 @@
 ---
-name: Non-rule change request
+name: "\U0001F4DD Non-rule change request"
 about: Request a change that is not a bug fix, rule change, or new rule
 
 ---
@@ -22,4 +22,3 @@ about: Request a change that is not a bug fix, rule change, or new rule
 **The problem you want to solve.**
 
 **Your take on the correct solution to problem.**
-
diff --git a/.github/ISSUE_TEMPLATE/NEW_RULE.md b/.github/ISSUE_TEMPLATE/NEW_RULE.md
index 454b28e40..3a04dc58a 100644
--- a/.github/ISSUE_TEMPLATE/NEW_RULE.md
+++ b/.github/ISSUE_TEMPLATE/NEW_RULE.md
@@ -1,5 +1,5 @@
 ---
-name: New rule proposal
+/name: "\U0001F680 New rule proposal"
 about: Propose a new rule to be added to ESLint
 
 ---
@@ -34,4 +34,3 @@ about: Propose a new rule to be added to ESLint
 ```
 
 **Why should this rule be included in ESLint (instead of a plugin)?**
-
diff --git a/.github/ISSUE_TEMPLATE/QUESTION.md b/.github/ISSUE_TEMPLATE/QUESTION.md
new file mode 100644
index 000000000..ba96b4104
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/QUESTION.md
@@ -0,0 +1,22 @@
+---
+name: "⛔ Question"
+about: Please go to https://gitter.im/eslint/eslint
+
+---
+
+<!--
+    ESLint adheres to the [JS Foundation Code of Conduct](https://js.foundation/community/code-of-conduct).
+
+
+    Please do not use GitHub for asking questions. Instead,
+    please visit our chatroom:
+
+        https://gitter.im/eslint/eslint
+
+    Or our mailing list:
+
+        https://groups.google.com/group/eslint
+
+    Thanks in advance for helping us keep the issue tracker
+    clean!
+-->
diff --git a/.github/ISSUE_TEMPLATE/RULE_CHANGE.md b/.github/ISSUE_TEMPLATE/RULE_CHANGE.md
index 7b0c65379..cc57bf532 100644
--- a/.github/ISSUE_TEMPLATE/RULE_CHANGE.md
+++ b/.github/ISSUE_TEMPLATE/RULE_CHANGE.md
@@ -1,5 +1,5 @@
 ---
-name: Rule change request
+name: "\U0001F4DD Rule change request"
 about: Request a change to an existing rule
 
 ---
@@ -33,4 +33,3 @@ about: Request a change to an existing rule
 **What does the rule currently do for this code?**
 
 **What will the rule do after it's changed?**
-
diff --git a/.github/ISSUE_TEMPLATE/SECURITY.md b/.github/ISSUE_TEMPLATE/SECURITY.md
new file mode 100644
index 000000000..8cc33ec6e
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/SECURITY.md
@@ -0,0 +1,19 @@
+---
+name: "⛔ Security issue"
+about: Please file security issues at https://hackerone.com/eslint
+
+---
+
+<!--
+    ESLint adheres to the [JS Foundation Code of Conduct](https://js.foundation/community/code-of-conduct).
+
+
+    STOP! Please do not use GitHub for filing security issues.
+    Doing so is insecure and may put other ESLint users at risk.
+    To securely notify us of a security issue, please go to:
+        
+        https://hackerone.com/eslint
+
+    Thanks in advance for helping us keep the ESLint community
+    safe and secure.
+-->
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0a372690c..2848b4c91 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,17 @@
+v5.6.1 - September 28, 2018
+
+* 9b26bdb Fix: avoid exponential require-atomic-updates traversal (fixes #10893) (#10894) (Teddy Katz)
+* 9432b10 Fix: make separateRequires work in consecutive mode (fixes #10784) (#10886) (Pig Fang)
+* e51868d Upgrade: debug@4 (fixes #10854) (#10887) (薛定谔的猫)
+* d3f3994 Docs: add information about reporting security issues (#10889) (Teddy Katz)
+* cc458f4 Build: fix failing tests on master (#10890) (Teddy Katz)
+* a6ebfd3 Docs: clarify defaultAssignment option, fix no-unneeded-ternary examples (#10874) (CoffeeTableEspresso)
+* 9d52541 Fix: Remove duplicate error message on crash (fixes #8964) (#10865) (Nicholas C. Zakas)
+* 4eb9a49 Docs: Update quotes.md (#10862) (The Jared Wilcurt)
+* 9159e9b Docs: Update complexity.md (#10867) (Szymon Przybylski)
+* 14f4e46 Docs: Use Linter instead of linter in Nodejs API page (#10864) (Nicholas C. Zakas)
+* b3e3cb1 Chore: Update debug log name to match filename (#10863) (Nicholas C. Zakas)
+
 v5.6.0 - September 14, 2018
 
 * c5b688e Update: Added generators option to func-names (fixes #9511) (#10697) (Oscar Barrett)
diff --git a/docs/rules/camelcase.md b/docs/rules/camelcase.md
index cce3fd924..1765f1852 100644
--- a/docs/rules/camelcase.md
+++ b/docs/rules/camelcase.md
@@ -14,6 +14,7 @@ This rule has an object option:
 * `"properties": "never"` does not check property names
 * `"ignoreDestructuring": false` (default) enforces camelcase style for destructured identifiers
 * `"ignoreDestructuring": true` does not check destructured identifiers
+* `allow` (`string[]`) list of properties to accept. Accept regex.
 
 ### properties: "always"
 
@@ -151,6 +152,30 @@ var { category_id = 1 } = query;
 var { category_id: category_id } = query;
 ```
 
+## allow
+
+Examples of **correct** code for this rule with the `allow` option:
+
+```js
+/*eslint camelcase: ["error", {allow: ["UNSAFE_componentWillMount"]}]*/
+
+function UNSAFE_componentWillMount() {
+    // ...
+}
+```
+
+```js
+/*eslint camelcase: ["error", {allow: ["^UNSAFE_"]}]*/
+
+function UNSAFE_componentWillMount() {
+    // ...
+}
+
+function UNSAFE_componentWillMount() {
+    // ...
+}
+```
+
 ## When Not To Use It
 
 If you have established coding standards using a different naming convention (separating words with underscores), turn this rule off.
diff --git a/karma.conf.js b/karma.conf.js
index 0ffab8bc5..29a8cdc8f 100644
--- a/karma.conf.js
+++ b/karma.conf.js
@@ -67,8 +67,13 @@ module.exports = function(config) {
 
         // start these browsers
         // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
-        browsers: ["PhantomJS"],
-
+        browsers: ["HeadlessChrome"],
+        customLaunchers: {
+          HeadlessChrome: {
+            base: 'ChromeHeadless',
+            flags: [ '--no-sandbox', ],
+             },
+            },
 
         // Continuous Integration mode
         // if true, Karma captures browsers, runs the tests and exits
diff --git a/lib/rules/camelcase.js b/lib/rules/camelcase.js
index 3005963ab..41040450f 100644
--- a/lib/rules/camelcase.js
+++ b/lib/rules/camelcase.js
@@ -27,6 +27,16 @@ module.exports = {
                     },
                     properties: {
                         enum: ["always", "never"]
+                    },
+                    allow: {
+                        type: "array",
+                        items: [
+                            {
+                                type: "string"
+                            }
+                        ],
+                        minItems: 0,
+                        uniqueItems: true
                     }
                 },
                 additionalProperties: false
@@ -40,6 +50,15 @@ module.exports = {
 
     create(context) {
 
+        const options = context.options[0] || {};
+        let properties = options.properties || "";
+        const ignoreDestructuring = options.ignoreDestructuring || false;
+        const allow = options.allow || [];
+
+        if (properties !== "always" && properties !== "never") {
+            properties = "always";
+        }
+
         //--------------------------------------------------------------------------
         // Helpers
         //--------------------------------------------------------------------------
@@ -60,6 +79,18 @@ module.exports = {
             return name.indexOf("_") > -1 && name !== name.toUpperCase();
         }
 
+        /**
+         * Checks if a string match the ignore list
+         * @param {string} name The string to check.
+         * @returns {boolean} if the string is ignored
+         * @private
+         */
+        function isAllowed(name) {
+            return allow.findIndex(
+                entry => name === entry || name.match(new RegExp(entry))
+            ) !== -1;
+        }
+
         /**
          * Checks if a parent of a node is an ObjectPattern.
          * @param {ASTNode} node The node to check.
@@ -93,14 +124,6 @@ module.exports = {
             }
         }
 
-        const options = context.options[0] || {};
-        let properties = options.properties || "";
-        const ignoreDestructuring = options.ignoreDestructuring || false;
-
-        if (properties !== "always" && properties !== "never") {
-            properties = "always";
-        }
-
         return {
 
             Identifier(node) {
@@ -112,6 +135,11 @@ module.exports = {
                 const name = node.name.replace(/^_+|_+$/g, ""),
                     effectiveParent = (node.parent.type === "MemberExpression") ? node.parent.parent : node.parent;
 
+                // First, we ignore the node if it match the ignore list
+                if (isAllowed(name)) {
+                    return;
+                }
+
                 // MemberExpressions get special rules
                 if (node.parent.type === "MemberExpression") {
 
diff --git a/lib/rules/one-var.js b/lib/rules/one-var.js
index 13ab72b04..15b664d77 100644
--- a/lib/rules/one-var.js
+++ b/lib/rules/one-var.js
@@ -384,8 +384,13 @@ module.exports = {
             if (nodeIndex > 0) {
                 const previousNode = parent.body[nodeIndex - 1];
                 const isPreviousNodeDeclaration = previousNode.type === "VariableDeclaration";
+                const declarationsWithPrevious = declarations.concat(previousNode.declarations || []);
 
-                if (isPreviousNodeDeclaration && previousNode.kind === type) {
+                if (
+                    isPreviousNodeDeclaration &&
+                    previousNode.kind === type &&
+                    !(declarationsWithPrevious.some(isRequire) && !declarationsWithPrevious.every(isRequire))
+                ) {
                     const previousDeclCounts = countDeclarations(previousNode.declarations);
 
                     if (options[type].initialized === MODE_CONSECUTIVE && options[type].uninitialized === MODE_CONSECUTIVE) {
diff --git a/lib/rules/require-atomic-updates.js b/lib/rules/require-atomic-updates.js
index 5d7d71514..c6cf0d747 100644
--- a/lib/rules/require-atomic-updates.js
+++ b/lib/rules/require-atomic-updates.js
@@ -119,6 +119,66 @@ module.exports = {
             });
         }
 
+        const alreadyReportedAssignments = new WeakSet();
+
+        class AssignmentTrackerState {
+            constructor({ openAssignmentsWithoutReads = new Set(), openAssignmentsWithReads = new Set() } = {}) {
+                this.openAssignmentsWithoutReads = openAssignmentsWithoutReads;
+                this.openAssignmentsWithReads = openAssignmentsWithReads;
+            }
+
+            copy() {
+                return new AssignmentTrackerState({
+                    openAssignmentsWithoutReads: new Set(this.openAssignmentsWithoutReads),
+                    openAssignmentsWithReads: new Set(this.openAssignmentsWithReads)
+                });
+            }
+
+            merge(other) {
+                const initialAssignmentsWithoutReadsCount = this.openAssignmentsWithoutReads.size;
+                const initialAssignmentsWithReadsCount = this.openAssignmentsWithReads.size;
+
+                other.openAssignmentsWithoutReads.forEach(assignment => this.openAssignmentsWithoutReads.add(assignment));
+                other.openAssignmentsWithReads.forEach(assignment => this.openAssignmentsWithReads.add(assignment));
+
+                return this.openAssignmentsWithoutReads.size > initialAssignmentsWithoutReadsCount ||
+                    this.openAssignmentsWithReads.size > initialAssignmentsWithReadsCount;
+            }
+
+            enterAssignment(assignmentExpression) {
+                (assignmentExpression.operator === "=" ? this.openAssignmentsWithoutReads : this.openAssignmentsWithReads).add(assignmentExpression);
+            }
+
+            exitAssignment(assignmentExpression) {
+                this.openAssignmentsWithoutReads.delete(assignmentExpression);
+                this.openAssignmentsWithReads.delete(assignmentExpression);
+            }
+
+            exitAwaitOrYield(node, surroundingFunction) {
+                return [...this.openAssignmentsWithReads]
+                    .filter(assignment => !isLocalVariableWithoutEscape(assignment.left, surroundingFunction))
+                    .forEach(assignment => {
+                        if (!alreadyReportedAssignments.has(assignment)) {
+                            reportAssignment(assignment);
+                            alreadyReportedAssignments.add(assignment);
+                        }
+                    });
+            }
+
+            exitIdentifierOrMemberExpression(node) {
+                [...this.openAssignmentsWithoutReads]
+                    .filter(assignment => (
+                        assignment.left !== node &&
+                        assignment.left.type === node.type &&
+                        astUtils.equalTokens(assignment.left, node, sourceCode)
+                    ))
+                    .forEach(assignment => {
+                        this.openAssignmentsWithoutReads.delete(assignment);
+                        this.openAssignmentsWithReads.add(assignment);
+                    });
+            }
+        }
+
         /**
          * If the control flow graph of a function enters an assignment expression, then does the
          * both of the following steps in order (possibly with other steps in between) before exiting the
@@ -135,54 +195,51 @@ module.exports = {
             codePathSegment,
             surroundingFunction,
             {
-                seenSegments = new Set(),
-                openAssignmentsWithoutReads = new Set(),
-                openAssignmentsWithReads = new Set()
+                stateBySegmentStart = new WeakMap(),
+                stateBySegmentEnd = new WeakMap()
             } = {}
         ) {
-            if (seenSegments.has(codePathSegment)) {
-
-                // An AssignmentExpression can't contain loops, so it's not necessary to reenter them with new state.
-                return;
+            if (!stateBySegmentStart.has(codePathSegment)) {
+                stateBySegmentStart.set(codePathSegment, new AssignmentTrackerState());
             }
 
+            const currentState = stateBySegmentStart.get(codePathSegment).copy();
+
             expressionsByCodePathSegment.get(codePathSegment).forEach(({ entering, node }) => {
                 if (node.type === "AssignmentExpression") {
                     if (entering) {
-                        (node.operator === "=" ? openAssignmentsWithoutReads : openAssignmentsWithReads).add(node);
+                        currentState.enterAssignment(node);
                     } else {
-                        openAssignmentsWithoutReads.delete(node);
-                        openAssignmentsWithReads.delete(node);
+                        currentState.exitAssignment(node);
                     }
                 } else if (!entering && (node.type === "AwaitExpression" || node.type === "YieldExpression")) {
-                    [...openAssignmentsWithReads]
-                        .filter(assignment => !isLocalVariableWithoutEscape(assignment.left, surroundingFunction))
-                        .forEach(reportAssignment);
-
-                    openAssignmentsWithReads.clear();
+                    currentState.exitAwaitOrYield(node, surroundingFunction);
                 } else if (!entering && (node.type === "Identifier" || node.type === "MemberExpression")) {
-                    [...openAssignmentsWithoutReads]
-                        .filter(assignment => (
-                            assignment.left !== node &&
-                            assignment.left.type === node.type &&
-                            astUtils.equalTokens(assignment.left, node, sourceCode)
-                        ))
-                        .forEach(assignment => {
-                            openAssignmentsWithoutReads.delete(assignment);
-                            openAssignmentsWithReads.add(assignment);
-                        });
+                    currentState.exitIdentifierOrMemberExpression(node);
                 }
             });
 
+            stateBySegmentEnd.set(codePathSegment, currentState);
+
             codePathSegment.nextSegments.forEach(nextSegment => {
+                if (stateBySegmentStart.has(nextSegment)) {
+                    if (!stateBySegmentStart.get(nextSegment).merge(currentState)) {
+
+                        /*
+                         * This segment has already been processed with the given set of inputs;
+                         * no need to do it again. After no new state is available to process
+                         * for any control flow segment in the graph, the analysis reaches a fixpoint and
+                         * traversal stops.
+                         */
+                        return;
+                    }
+                } else {
+                    stateBySegmentStart.set(nextSegment, currentState.copy());
+                }
                 findOutdatedReads(
                     nextSegment,
                     surroundingFunction,
-                    {
-                        seenSegments: new Set(seenSegments).add(codePathSegment),
-                        openAssignmentsWithoutReads: new Set(openAssignmentsWithoutReads),
-                        openAssignmentsWithReads: new Set(openAssignmentsWithReads)
-                    }
+                    { stateBySegmentStart, stateBySegmentEnd }
                 );
             });
         }
diff --git a/package.json b/package.json
index 1a37b3c21..e970cf87c 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "eslint",
-  "version": "5.6.0",
+  "version": "5.6.1",
   "author": "Nicholas C. Zakas <nicholas+npm@nczconsulting.com>",
   "description": "An AST-based pattern checker for JavaScript.",
   "bin": {
@@ -39,7 +39,7 @@
     "ajv": "^6.5.3",
     "chalk": "^2.1.0",
     "cross-spawn": "^6.0.5",
-    "debug": "^3.1.0",
+    "debug": "^4.0.1",
     "doctrine": "^2.1.0",
     "eslint-scope": "^4.0.0",
     "eslint-utils": "^1.3.1",
@@ -71,7 +71,7 @@
     "semver": "^5.5.1",
     "strip-ansi": "^4.0.0",
     "strip-json-comments": "^2.0.1",
-    "table": "^4.0.3",
+    "table": "^5.0.2",
     "text-table": "^0.2.0"
   },
   "devDependencies": {
@@ -100,14 +100,13 @@
     "karma-babel-preprocessor": "^7.0.0",
     "karma-mocha": "^1.3.0",
     "karma-mocha-reporter": "^2.2.3",
-    "karma-phantomjs-launcher": "^1.0.4",
+    "karma-chrome-launcher": "^2.2.0",
     "leche": "^2.2.3",
     "load-perf": "^0.2.0",
     "markdownlint": "^0.11.0",
     "mocha": "^5.0.5",
     "mock-fs": "^4.6.0",
     "npm-license": "^0.3.3",
-    "phantomjs-prebuilt": "^2.1.16",
     "proxyquire": "^2.0.1",
     "shelljs": "^0.8.2",
     "sinon": "^3.3.0",


Test patch:
diff --git a/tests/lib/rules/camelcase.js b/tests/lib/rules/camelcase.js
index 481842f97..3d2b7516c 100644
--- a/tests/lib/rules/camelcase.js
+++ b/tests/lib/rules/camelcase.js
@@ -181,6 +181,18 @@ ruleTester.run("camelcase", rule, {
         {
             code: "function foo({ trailing_ }) {}",
             parserOptions: { ecmaVersion: 6 }
+        },
+        {
+            code: "ignored_foo = 0;",
+            options: [{ allow: ["ignored_foo"] }]
+        },
+        {
+            code: "ignored_foo = 0; ignored_bar = 1;",
+            options: [{ allow: ["ignored_foo", "ignored_bar"] }]
+        },
+        {
+            code: "user_id = 0;",
+            options: [{ allow: ["_id$"] }]
         }
     ],
     invalid: [
@@ -555,6 +567,26 @@ ruleTester.run("camelcase", rule, {
                     type: "Identifier"
                 }
             ]
+        },
+        {
+            code: "not_ignored_foo = 0;",
+            options: [{ allow: ["ignored_bar"] }],
+            errors: [
+                {
+                    message: "Identifier 'not_ignored_foo' is not in camel case.",
+                    type: "Identifier"
+                }
+            ]
+        },
+        {
+            code: "not_ignored_foo = 0;",
+            options: [{ allow: ["_id$"] }],
+            errors: [
+                {
+                    message: "Identifier 'not_ignored_foo' is not in camel case.",
+                    type: "Identifier"
+                }
+            ]
         }
     ]
 });
diff --git a/tests/lib/rules/one-var.js b/tests/lib/rules/one-var.js
index 1f6626ad8..b628f512a 100644
--- a/tests/lib/rules/one-var.js
+++ b/tests/lib/rules/one-var.js
@@ -263,6 +263,13 @@ ruleTester.run("one-var", rule, {
             options: ["consecutive"],
             parserOptions: { ecmaVersion: 6 }
         },
+
+        // https://github.com/eslint/eslint/issues/10784
+        {
+            code: "const foo = require('foo'); const bar = 'bar';",
+            options: [{ const: "consecutive", separateRequires: true }],
+            parserOptions: { ecmaVersion: 6 }
+        },
         {
             code: "var a = 0, b = 1; var c, d;",
             options: [{ initialized: "consecutive", uninitialized: "always" }]
diff --git a/tests/lib/rules/require-atomic-updates.js b/tests/lib/rules/require-atomic-updates.js
index eabd52149..412835777 100644
--- a/tests/lib/rules/require-atomic-updates.js
+++ b/tests/lib/rules/require-atomic-updates.js
@@ -51,7 +51,64 @@ ruleTester.run("require-atomic-updates", rule, {
         "let foo; function* x() { foo = bar + foo; }",
         "async function x() { let foo; bar(() => baz += 1); foo += await amount; }",
         "let foo; async function x() { foo = condition ? foo : await bar; }",
-        "async function x() { let foo; bar(() => { let foo; blah(foo); }); foo += await result; }"
+        "async function x() { let foo; bar(() => { let foo; blah(foo); }); foo += await result; }",
+        "let foo; async function x() { foo = foo + 1; await bar; }",
+
+
+        /*
+         * Ensure rule doesn't take exponential time in the number of branches
+         * (see https://github.com/eslint/eslint/issues/10893)
+         */
+        `
+            async function foo() {
+                if (1);
+                if (2);
+                if (3);
+                if (4);
+                if (5);
+                if (6);
+                if (7);
+                if (8);
+                if (9);
+                if (10);
+                if (11);
+                if (12);
+                if (13);
+                if (14);
+                if (15);
+                if (16);
+                if (17);
+                if (18);
+                if (19);
+                if (20);
+            }
+        `,
+        `
+            async function foo() {
+                return [
+                    1 ? a : b,
+                    2 ? a : b,
+                    3 ? a : b,
+                    4 ? a : b,
+                    5 ? a : b,
+                    6 ? a : b,
+                    7 ? a : b,
+                    8 ? a : b,
+                    9 ? a : b,
+                    10 ? a : b,
+                    11 ? a : b,
+                    12 ? a : b,
+                    13 ? a : b,
+                    14 ? a : b,
+                    15 ? a : b,
+                    16 ? a : b,
+                    17 ? a : b,
+                    18 ? a : b,
+                    19 ? a : b,
+                    20 ? a : b
+                ];
+            }
+        `
     ],
 
     invalid: [
@@ -59,6 +116,10 @@ ruleTester.run("require-atomic-updates", rule, {
             code: "let foo; async function x() { foo += await amount; }",
             errors: [{ messageId: "nonAtomicUpdate", data: { value: "foo" } }]
         },
+        {
+            code: "if (1); let foo; async function x() { foo += await amount; }",
+            errors: [{ messageId: "nonAtomicUpdate", data: { value: "foo" } }]
+        },
         {
             code: "let foo; async function x() { while (condition) { foo += await amount; } }",
             errors: [VARIABLE_ERROR]
@@ -138,6 +199,14 @@ ruleTester.run("require-atomic-updates", rule, {
         {
             code: "async function x() { foo += await bar; }",
             errors: [VARIABLE_ERROR]
+        },
+        {
+            code: "let foo = 0; async function x() { foo = (a ? b : foo) + await bar; if (baz); }",
+            errors: [VARIABLE_ERROR]
+        },
+        {
+            code: "let foo = 0; async function x() { foo = (a ? b ? c ? d ? foo : e : f : g : h) + await bar; if (baz); }",
+            errors: [VARIABLE_ERROR]
         }
     ]
 });