Repository: eslint/eslint (js)

Issue:
New option in no-unsafe-negation to report ! a < b 
<!--
    ESLint adheres to the [JS Foundation Code of Conduct](https://js.foundation/community/code-of-conduct).

    This template is for requesting a rule change. If you are here for another reason, please see below:

    1. To report a bug: https://eslint.org/docs/developer-guide/contributing/reporting-bugs
    2. To propose a new rule: https://eslint.org/docs/developer-guide/contributing/new-rules
    3. To request a change that is not a bug fix, rule change, or new rule: https://eslint.org/docs/developer-guide/contributing/changes
    4. If you have any questions, please stop by our chatroom: https://gitter.im/eslint/eslint

    Note that leaving sections blank will make it difficult for us to troubleshoot and we may have to close the issue.
-->

**What rule do you want to change?**

`no-unsafe-negation`

> This rule disallows negating the left operand of Relational Operators. 

The rule currently targets only `in` and `instanceof`.

`<`, `>`, `<=`, `>=` are also [relational operators](https://www.ecma-international.org/ecma-262/10.0/index.html#sec-relational-operators).

`! a < b` is equivalent to `(a ? 0 : 1) < b` (I think), which is rarely the intended meaning:

```js
function min(a, b) {
    let m = a;
    if (! a < b) {
        m = b;
    }
    return m;
}

min(1, 2); // 2
```

**Does this change cause the rule to produce more or fewer warnings?**

More if the option is set to `true`. Default is `false`.

**How will the change be implemented? (New option, new default behavior, etc.)?**

New option.

**Please provide some example code that this change will affect:**

<!-- Put your code examples here -->
```js
/*eslint no-unsafe-negation: "error"*/

if (! a < b) {}

while (! a > b) {}

foo = ! a <= b;

foo = ! a >= b;
```

**What does the rule currently do for this code?**

Nothing.

**What will the rule do after it's changed?**

4 errors

```js
/*eslint no-unsafe-negation: ["error", { "enforceForOrderingRelations": true }]*/

if (! a < b) {} // error

while (! a > b) {} // error

foo = ! a <= b; // error

foo = ! a >= b; // error
```

**Are you willing to submit a pull request to implement this change?**

Yes.

Pull request:
Update: enforceForOrderingRelations no-unsafe-negation (fixes #12163)
<!--
    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
[ ] 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))
[X] 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.)
-->

<!--
    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)**
Add `enforceForOrderingRelations` option to no-unsafe-negation rule. See:
https://github.com/eslint/eslint/issues/12163

**Is there anything you'd like reviewers to focus on?**




Patch:
diff --git a/docs/rules/no-extra-parens.md b/docs/rules/no-extra-parens.md
index 1896d0a9e..4cadaa757 100644
--- a/docs/rules/no-extra-parens.md
+++ b/docs/rules/no-extra-parens.md
@@ -25,6 +25,7 @@ This rule has an object option for exceptions to the `"all"` option:
 * `"ignoreJSX": "none|all|multi-line|single-line"` allows extra parentheses around no/all/multi-line/single-line JSX components. Defaults to `none`.
 * `"enforceForArrowConditionals": false` allows extra parentheses around ternary expressions which are the body of an arrow function
 * `"enforceForSequenceExpressions": false` allows extra parentheses around sequence expressions
+* `"enforceForNewInMemberExpressions": false` allows extra parentheses around `new` expressions in member expressions
 
 ### all
 
@@ -207,6 +208,20 @@ if ((val = foo(), val < 10)) {}
 while ((val = foo(), val < 10));
 ```
 
+### enforceForNewInMemberExpressions
+
+Examples of **correct** code for this rule with the `"all"` and `{ "enforceForNewInMemberExpressions": false }` options:
+
+```js
+/* eslint no-extra-parens: ["error", "all", { "enforceForNewInMemberExpressions": false }] */
+
+const foo = (new Bar()).baz;
+
+const quux = (new Bar())[baz];
+
+(new Bar()).doSomething();
+```
+
 ### functions
 
 Examples of **incorrect** code for this rule with the `"functions"` option:
diff --git a/docs/rules/no-unsafe-negation.md b/docs/rules/no-unsafe-negation.md
index 880e9f644..38bb9b02f 100644
--- a/docs/rules/no-unsafe-negation.md
+++ b/docs/rules/no-unsafe-negation.md
@@ -4,12 +4,10 @@ Just as developers might type `-a + b` when they mean `-(a + b)` for the negativ
 
 ## Rule Details
 
-This rule disallows negating the left operand of [Relational Operators](https://developer.mozilla.org/en/docs/Web/JavaScript/Guide/Expressions_and_Operators#Relational_operators).
+This rule disallows negating the left operand of the following relational operators:
 
-Relational Operators are:
-
-- `in` operator.
-- `instanceof` operator.
+- [`in` operator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/in).
+- [`instanceof` operator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/instanceof).
 
 Examples of **incorrect** code for this rule:
 
@@ -72,6 +70,38 @@ if (!(foo) in object) {
 }
 ```
 
+## Options
+
+This rule has an object option:
+
+- `"enforceForOrderingRelations": false` (default) allows negation of the left-hand side of ordering relational operators (`<`, `>`, `<=`, `>=`)
+- `"enforceForOrderingRelations": true` disallows negation of the left-hand side of ordering relational operators
+
+### enforceForOrderingRelations
+
+With this option set to `true` the rule is additionally enforced for:
+
+- `<` operator.
+- `>` operator.
+- `<=` operator.
+- `>=` operator.
+
+The purpose is to avoid expressions such as `! a < b` (which is equivalent to `(a ? 0 : 1) < b`) when what is really intended is `!(a < b)`.
+
+Examples of additional **incorrect** code for this rule with the `{ "enforceForOrderingRelations": true }` option:
+
+```js
+/*eslint no-unsafe-negation: ["error", { "enforceForOrderingRelations": true }]*/
+
+if (! a < b) {}
+
+while (! a > b) {}
+
+foo = ! a <= b;
+
+foo = ! a >= b;
+```
+
 ## When Not To Use It
 
 If you don't want to notify unsafe logical negations, then it's safe to disable this rule.
diff --git a/lib/rules/array-bracket-newline.js b/lib/rules/array-bracket-newline.js
index 214631777..b4b4dd430 100644
--- a/lib/rules/array-bracket-newline.js
+++ b/lib/rules/array-bracket-newline.js
@@ -216,7 +216,7 @@ module.exports = {
                 ) ||
                 (
                     options.consistent &&
-                    firstIncComment.loc.start.line !== openBracket.loc.end.line
+                    openBracket.loc.end.line !== first.loc.start.line
                 )
             );
 
diff --git a/lib/rules/dot-location.js b/lib/rules/dot-location.js
index c2e734a1a..812988efa 100644
--- a/lib/rules/dot-location.js
+++ b/lib/rules/dot-location.js
@@ -67,7 +67,7 @@ module.exports = {
 
                     context.report({
                         node,
-                        loc: dot.loc.start,
+                        loc: dot.loc,
                         messageId: "expectedDotAfterObject",
                         fix: fixer => fixer.replaceTextRange([tokenBeforeDot.range[1], prop.range[0]], `${neededTextAfterToken}.${textBeforeDot}${textAfterDot}`)
                     });
@@ -75,7 +75,7 @@ module.exports = {
             } else if (!astUtils.isTokenOnSameLine(dot, prop)) {
                 context.report({
                     node,
-                    loc: dot.loc.start,
+                    loc: dot.loc,
                     messageId: "expectedDotBeforeProperty",
                     fix: fixer => fixer.replaceTextRange([tokenBeforeDot.range[1], prop.range[0]], `${textBeforeDot}${textAfterDot}.`)
                 });
diff --git a/lib/rules/no-extra-parens.js b/lib/rules/no-extra-parens.js
index 677cde56b..f96e572bf 100644
--- a/lib/rules/no-extra-parens.js
+++ b/lib/rules/no-extra-parens.js
@@ -50,7 +50,8 @@ module.exports = {
                                 returnAssign: { type: "boolean" },
                                 ignoreJSX: { enum: ["none", "all", "single-line", "multi-line"] },
                                 enforceForArrowConditionals: { type: "boolean" },
-                                enforceForSequenceExpressions: { type: "boolean" }
+                                enforceForSequenceExpressions: { type: "boolean" },
+                                enforceForNewInMemberExpressions: { type: "boolean" }
                             },
                             additionalProperties: false
                         }
@@ -80,6 +81,8 @@ module.exports = {
             context.options[1].enforceForArrowConditionals === false;
         const IGNORE_SEQUENCE_EXPRESSIONS = ALL_NODES && context.options[1] &&
             context.options[1].enforceForSequenceExpressions === false;
+        const IGNORE_NEW_IN_MEMBER_EXPR = ALL_NODES && context.options[1] &&
+            context.options[1].enforceForNewInMemberExpressions === false;
 
         const PRECEDENCE_OF_ASSIGNMENT_EXPR = precedence({ type: "AssignmentExpression" });
         const PRECEDENCE_OF_UPDATE_EXPR = precedence({ type: "UpdateExpression" });
@@ -893,6 +896,7 @@ module.exports = {
                 }
 
                 if (nodeObjHasExcessParens &&
+                  !IGNORE_NEW_IN_MEMBER_EXPR &&
                   node.object.type === "NewExpression" &&
                   isNewExpressionWithParens(node.object)) {
                     report(node.object);
diff --git a/lib/rules/no-mixed-operators.js b/lib/rules/no-mixed-operators.js
index 9e4cbd6e2..80fac79af 100644
--- a/lib/rules/no-mixed-operators.js
+++ b/lib/rules/no-mixed-operators.js
@@ -197,13 +197,13 @@ module.exports = {
 
             context.report({
                 node: left,
-                loc: getOperatorToken(left).loc.start,
+                loc: getOperatorToken(left).loc,
                 message,
                 data
             });
             context.report({
                 node: right,
-                loc: getOperatorToken(right).loc.start,
+                loc: getOperatorToken(right).loc,
                 message,
                 data
             });
diff --git a/lib/rules/no-unsafe-negation.js b/lib/rules/no-unsafe-negation.js
index 5226b367d..5c8f569d7 100644
--- a/lib/rules/no-unsafe-negation.js
+++ b/lib/rules/no-unsafe-negation.js
@@ -16,14 +16,23 @@ const astUtils = require("./utils/ast-utils");
 //------------------------------------------------------------------------------
 
 /**
- * Checks whether the given operator is a relational operator or not.
+ * Checks whether the given operator is `in` or `instanceof`
  * @param {string} op The operator type to check.
- * @returns {boolean} `true` if the operator is a relational operator.
+ * @returns {boolean} `true` if the operator is `in` or `instanceof`
  */
-function isRelationalOperator(op) {
+function isInOrInstanceOfOperator(op) {
     return op === "in" || op === "instanceof";
 }
 
+/**
+ * Checks whether the given operator is an ordering relational operator or not.
+ * @param {string} op The operator type to check.
+ * @returns {boolean} `true` if the operator is an ordering relational operator.
+ */
+function isOrderingRelationalOperator(op) {
+    return op === "<" || op === ">" || op === ">=" || op === "<=";
+}
+
 /**
  * Checks whether the given node is a logical negation expression or not.
  * @param {ASTNode} node The node to check.
@@ -48,7 +57,18 @@ module.exports = {
             url: "https://eslint.org/docs/rules/no-unsafe-negation"
         },
 
-        schema: [],
+        schema: [
+            {
+                type: "object",
+                properties: {
+                    enforceForOrderingRelations: {
+                        type: "boolean",
+                        default: false
+                    }
+                },
+                additionalProperties: false
+            }
+        ],
         fixable: null,
         messages: {
             unexpected: "Unexpected negating the left operand of '{{operator}}' operator."
@@ -57,10 +77,15 @@ module.exports = {
 
     create(context) {
         const sourceCode = context.getSourceCode();
+        const options = context.options[0] || {};
+        const enforceForOrderingRelations = options.enforceForOrderingRelations === true;
 
         return {
             BinaryExpression(node) {
-                if (isRelationalOperator(node.operator) &&
+                const orderingRelationRuleApplies = enforceForOrderingRelations && isOrderingRelationalOperator(node.operator);
+
+                if (
+                    (isInOrInstanceOfOperator(node.operator) || orderingRelationRuleApplies) &&
                     isNegation(node.left) &&
                     !astUtils.isParenthesised(sourceCode, node.left)
                 ) {
diff --git a/lib/rules/prefer-numeric-literals.js b/lib/rules/prefer-numeric-literals.js
index b4113a3b3..672d9bcaa 100644
--- a/lib/rules/prefer-numeric-literals.js
+++ b/lib/rules/prefer-numeric-literals.js
@@ -5,6 +5,12 @@
 
 "use strict";
 
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const astUtils = require("./utils/ast-utils");
+
 //------------------------------------------------------------------------------
 // Helpers
 //------------------------------------------------------------------------------
@@ -94,13 +100,13 @@ module.exports = {
                             functionName: sourceCode.getText(node.callee)
                         },
                         fix(fixer) {
-                            const newPrefix = prefixMap[node.arguments[1].value];
-
                             if (sourceCode.getCommentsInside(node).length) {
                                 return null;
                             }
 
-                            if (+(newPrefix + node.arguments[0].value) !== parseInt(node.arguments[0].value, node.arguments[1].value)) {
+                            const replacement = `${prefixMap[node.arguments[1].value]}${node.arguments[0].value}`;
+
+                            if (+replacement !== parseInt(node.arguments[0].value, node.arguments[1].value)) {
 
                                 /*
                                  * If the newly-produced literal would be invalid, (e.g. 0b1234),
@@ -108,7 +114,29 @@ module.exports = {
                                  */
                                 return null;
                             }
-                            return fixer.replaceText(node, prefixMap[node.arguments[1].value] + node.arguments[0].value);
+
+                            const tokenBefore = sourceCode.getTokenBefore(node),
+                                tokenAfter = sourceCode.getTokenAfter(node);
+                            let prefix = "",
+                                suffix = "";
+
+                            if (
+                                tokenBefore &&
+                                tokenBefore.range[1] === node.range[0] &&
+                                !astUtils.canTokensBeAdjacent(tokenBefore, replacement)
+                            ) {
+                                prefix = " ";
+                            }
+
+                            if (
+                                tokenAfter &&
+                                node.range[1] === tokenAfter.range[0] &&
+                                !astUtils.canTokensBeAdjacent(replacement, tokenAfter)
+                            ) {
+                                suffix = " ";
+                            }
+
+                            return fixer.replaceText(node, `${prefix}${replacement}${suffix}`);
                         }
                     });
                 }
diff --git a/lib/source-code/source-code.js b/lib/source-code/source-code.js
index 450dc24a1..86a56803e 100644
--- a/lib/source-code/source-code.js
+++ b/lib/source-code/source-code.js
@@ -423,7 +423,7 @@ class SourceCode extends TokenStore {
     isSpaceBetweenTokens(first, second) {
         const text = this.text.slice(first.range[1], second.range[0]);
 
-        return /\s/u.test(text.replace(/\/\*.*?\*\//gu, ""));
+        return /\s/u.test(text.replace(/\/\*.*?\*\//gus, ""));
     }
 
     /**


Test patch:
diff --git a/tests/lib/rules/array-bracket-newline.js b/tests/lib/rules/array-bracket-newline.js
index b1603fcd5..d20c365db 100644
--- a/tests/lib/rules/array-bracket-newline.js
+++ b/tests/lib/rules/array-bracket-newline.js
@@ -65,6 +65,10 @@ ruleTester.run("array-bracket-newline", rule, {
         { code: "var a = [\n]", options: ["consistent"] },
         { code: "var a = [1]", options: ["consistent"] },
         { code: "var a = [\n1\n]", options: ["consistent"] },
+        { code: "var a = [//\n1\n]", options: ["consistent"] },
+        { code: "var a = [/**/\n1\n]", options: ["consistent"] },
+        { code: "var a = [/*\n*/1\n]", options: ["consistent"] },
+        { code: "var a = [//\n]", options: ["consistent"] },
 
         // { multiline: true }
         { code: "var foo = [];", options: [{ multiline: true }] },
@@ -156,6 +160,10 @@ ruleTester.run("array-bracket-newline", rule, {
         { code: "var [\n] = foo", options: ["consistent"], parserOptions: { ecmaVersion: 6 } },
         { code: "var [a] = foo", options: ["consistent"], parserOptions: { ecmaVersion: 6 } },
         { code: "var [\na\n] = foo", options: ["consistent"], parserOptions: { ecmaVersion: 6 } },
+        { code: "var [//\na\n] = foo", options: ["consistent"], parserOptions: { ecmaVersion: 6 } },
+        { code: "var [/**/\na\n] = foo", options: ["consistent"], parserOptions: { ecmaVersion: 6 } },
+        { code: "var [/*\n*/a\n] = foo", options: ["consistent"], parserOptions: { ecmaVersion: 6 } },
+        { code: "var [//\n] = foo", options: ["consistent"], parserOptions: { ecmaVersion: 6 } },
 
         // { multiline: true }
         { code: "var [] = foo;", options: [{ multiline: true }], parserOptions: { ecmaVersion: 6 } },
@@ -529,6 +537,21 @@ ruleTester.run("array-bracket-newline", rule, {
                 }
             ]
         },
+        {
+            code: "var foo = [//\n1]",
+            output: "var foo = [//\n1\n]",
+            options: ["consistent"],
+            errors: [
+                {
+                    messageId: "missingClosingLinebreak",
+                    type: "ArrayExpression",
+                    line: 2,
+                    column: 2,
+                    endLine: 2,
+                    endColumn: 3
+                }
+            ]
+        },
 
         // { multiline: true }
         {
@@ -1459,6 +1482,22 @@ ruleTester.run("array-bracket-newline", rule, {
                 }
             ]
         },
+        {
+            code: "var [//\na] = foo",
+            output: "var [//\na\n] = foo",
+            options: ["consistent"],
+            parserOptions: { ecmaVersion: 6 },
+            errors: [
+                {
+                    messageId: "missingClosingLinebreak",
+                    type: "ArrayPattern",
+                    line: 2,
+                    column: 2,
+                    endLine: 2,
+                    endColumn: 3
+                }
+            ]
+        },
 
         // { minItems: 2 }
         {
diff --git a/tests/lib/rules/dot-location.js b/tests/lib/rules/dot-location.js
index 3f6caea60..1a6ea37a7 100644
--- a/tests/lib/rules/dot-location.js
+++ b/tests/lib/rules/dot-location.js
@@ -143,13 +143,13 @@ ruleTester.run("dot-location", rule, {
             code: "obj\n.property",
             output: "obj.\nproperty",
             options: ["object"],
-            errors: [{ messageId: "expectedDotAfterObject", type: "MemberExpression", line: 2, column: 1 }]
+            errors: [{ messageId: "expectedDotAfterObject", type: "MemberExpression", line: 2, column: 1, endLine: 2, endColumn: 2 }]
         },
         {
             code: "obj.\nproperty",
             output: "obj\n.property",
             options: ["property"],
-            errors: [{ messageId: "expectedDotBeforeProperty", type: "MemberExpression", line: 1, column: 4 }]
+            errors: [{ messageId: "expectedDotBeforeProperty", type: "MemberExpression", line: 1, column: 4, endLine: 1, endColumn: 5 }]
         },
         {
             code: "(obj).\nproperty",
diff --git a/tests/lib/rules/no-extra-parens.js b/tests/lib/rules/no-extra-parens.js
index e42b5eadc..2cda39104 100644
--- a/tests/lib/rules/no-extra-parens.js
+++ b/tests/lib/rules/no-extra-parens.js
@@ -456,6 +456,14 @@ ruleTester.run("no-extra-parens", rule, {
         { code: "if((a, b)){}", options: ["all", { enforceForSequenceExpressions: false }] },
         { code: "while ((val = foo(), val < 10));", options: ["all", { enforceForSequenceExpressions: false }] },
 
+        // ["all", { enforceForNewInMemberExpressions: false }]
+        { code: "(new foo()).bar", options: ["all", { enforceForNewInMemberExpressions: false }] },
+        { code: "(new foo())[bar]", options: ["all", { enforceForNewInMemberExpressions: false }] },
+        { code: "(new foo()).bar()", options: ["all", { enforceForNewInMemberExpressions: false }] },
+        { code: "(new foo(bar)).baz", options: ["all", { enforceForNewInMemberExpressions: false }] },
+        { code: "(new foo.bar()).baz", options: ["all", { enforceForNewInMemberExpressions: false }] },
+        { code: "(new foo.bar()).baz()", options: ["all", { enforceForNewInMemberExpressions: false }] },
+
         "let a = [ ...b ]",
         "let a = { ...b }",
         {
@@ -659,6 +667,7 @@ ruleTester.run("no-extra-parens", rule, {
         invalid("(foo.bar()).baz", "foo.bar().baz", "CallExpression"),
         invalid("(foo\n.bar())\n.baz", "foo\n.bar()\n.baz", "CallExpression"),
         invalid("(new foo()).bar", "new foo().bar", "NewExpression"),
+        invalid("(new foo())[bar]", "new foo()[bar]", "NewExpression"),
         invalid("(new foo()).bar()", "new foo().bar()", "NewExpression"),
         invalid("(new foo(bar)).baz", "new foo(bar).baz", "NewExpression"),
         invalid("(new foo.bar()).baz", "new foo.bar().baz", "NewExpression"),
@@ -1165,6 +1174,63 @@ ruleTester.run("no-extra-parens", rule, {
             ]
         },
 
+        // ["all", { enforceForNewInMemberExpressions: true }]
+        {
+            code: "(new foo()).bar",
+            output: "new foo().bar",
+            options: ["all"],
+            errors: [
+                {
+                    messageId: "unexpected",
+                    type: "NewExpression"
+                }
+            ]
+        },
+        {
+            code: "(new foo()).bar",
+            output: "new foo().bar",
+            options: ["all", {}],
+            errors: [
+                {
+                    messageId: "unexpected",
+                    type: "NewExpression"
+                }
+            ]
+        },
+        {
+            code: "(new foo()).bar",
+            output: "new foo().bar",
+            options: ["all", { enforceForNewInMemberExpressions: true }],
+            errors: [
+                {
+                    messageId: "unexpected",
+                    type: "NewExpression"
+                }
+            ]
+        },
+        {
+            code: "(new foo())[bar]",
+            output: "new foo()[bar]",
+            options: ["all", { enforceForNewInMemberExpressions: true }],
+            errors: [
+                {
+                    messageId: "unexpected",
+                    type: "NewExpression"
+                }
+            ]
+        },
+        {
+            code: "(new foo.bar()).baz",
+            output: "new foo.bar().baz",
+            options: ["all", { enforceForNewInMemberExpressions: true }],
+            errors: [
+                {
+                    messageId: "unexpected",
+                    type: "NewExpression"
+                }
+            ]
+        },
+
         // https://github.com/eslint/eslint/issues/8175
         invalid(
             "let a = [...(b)]",
diff --git a/tests/lib/rules/no-mixed-operators.js b/tests/lib/rules/no-mixed-operators.js
index 4e822181f..efc93bf79 100644
--- a/tests/lib/rules/no-mixed-operators.js
+++ b/tests/lib/rules/no-mixed-operators.js
@@ -64,8 +64,8 @@ ruleTester.run("no-mixed-operators", rule, {
         {
             code: "a && b || c",
             errors: [
-                { column: 3, message: "Unexpected mix of '&&' and '||'." },
-                { column: 8, message: "Unexpected mix of '&&' and '||'." }
+                { column: 3, endColumn: 5, message: "Unexpected mix of '&&' and '||'." },
+                { column: 8, endColumn: 10, message: "Unexpected mix of '&&' and '||'." }
             ]
         },
         {
@@ -110,48 +110,48 @@ ruleTester.run("no-mixed-operators", rule, {
             code: "a + b - c",
             options: [{ allowSamePrecedence: false }],
             errors: [
-                { column: 3, message: "Unexpected mix of '+' and '-'." },
-                { column: 7, message: "Unexpected mix of '+' and '-'." }
+                { column: 3, endColumn: 4, message: "Unexpected mix of '+' and '-'." },
+                { column: 7, endColumn: 8, message: "Unexpected mix of '+' and '-'." }
             ]
         },
         {
             code: "a * b / c",
             options: [{ allowSamePrecedence: false }],
             errors: [
-                { column: 3, message: "Unexpected mix of '*' and '/'." },
-                { column: 7, message: "Unexpected mix of '*' and '/'." }
+                { column: 3, endColumn: 4, message: "Unexpected mix of '*' and '/'." },
+                { column: 7, endColumn: 8, message: "Unexpected mix of '*' and '/'." }
             ]
         },
         {
             code: "a || b ? c : d",
             options: [{ groups: [["&&", "||", "?:"]] }],
             errors: [
-                { column: 3, message: "Unexpected mix of '||' and '?:'." },
-                { column: 8, message: "Unexpected mix of '||' and '?:'." }
+                { column: 3, endColumn: 5, message: "Unexpected mix of '||' and '?:'." },
+                { column: 8, endColumn: 9, message: "Unexpected mix of '||' and '?:'." }
             ]
         },
         {
             code: "a && b ? 1 : 2",
             options: [{ groups: [["&&", "||", "?:"]] }],
             errors: [
-                { column: 3, message: "Unexpected mix of '&&' and '?:'." },
-                { column: 8, message: "Unexpected mix of '&&' and '?:'." }
+                { column: 3, endColumn: 5, message: "Unexpected mix of '&&' and '?:'." },
+                { column: 8, endColumn: 9, message: "Unexpected mix of '&&' and '?:'." }
             ]
         },
         {
             code: "x ? a && b : 0",
             options: [{ groups: [["&&", "||", "?:"]] }],
             errors: [
-                { column: 3, message: "Unexpected mix of '?:' and '&&'." },
-                { column: 7, message: "Unexpected mix of '?:' and '&&'." }
+                { column: 3, endColumn: 4, message: "Unexpected mix of '?:' and '&&'." },
+                { column: 7, endColumn: 9, message: "Unexpected mix of '?:' and '&&'." }
             ]
         },
         {
             code: "x ? 0 : a && b",
             options: [{ groups: [["&&", "||", "?:"]] }],
             errors: [
-                { column: 3, message: "Unexpected mix of '?:' and '&&'." },
-                { column: 11, message: "Unexpected mix of '?:' and '&&'." }
+                { column: 3, endColumn: 4, message: "Unexpected mix of '?:' and '&&'." },
+                { column: 11, endColumn: 13, message: "Unexpected mix of '?:' and '&&'." }
             ]
         }
     ]
diff --git a/tests/lib/rules/no-unsafe-negation.js b/tests/lib/rules/no-unsafe-negation.js
index 6ac245412..9493bbb72 100644
--- a/tests/lib/rules/no-unsafe-negation.js
+++ b/tests/lib/rules/no-unsafe-negation.js
@@ -18,7 +18,26 @@ const rule = require("../../../lib/rules/no-unsafe-negation"),
 
 const ruleTester = new RuleTester();
 const unexpectedInError = { messageId: "unexpected", data: { operator: "in" } };
-const unexpectedInstanceofError = { messageId: "unexpected", data: { operator: "instanceof" } };
+const unexpectedInstanceofError = {
+    messageId: "unexpected",
+    data: { operator: "instanceof" }
+};
+const unexpectedLessThanOperatorError = {
+    messageId: "unexpected",
+    data: { operator: "<" }
+};
+const unexpectedMoreThanOperatorError = {
+    messageId: "unexpected",
+    data: { operator: ">" }
+};
+const unexpectedMoreThanOrEqualOperatorError = {
+    messageId: "unexpected",
+    data: { operator: ">=" }
+};
+const unexpectedLessThanOrEqualOperatorError = {
+    messageId: "unexpected",
+    data: { operator: "<=" }
+};
 
 ruleTester.run("no-unsafe-negation", rule, {
     valid: [
@@ -29,7 +48,37 @@ ruleTester.run("no-unsafe-negation", rule, {
         "a instanceof b",
         "a instanceof b === false",
         "!(a instanceof b)",
-        "(!a) instanceof b"
+        "(!a) instanceof b",
+
+        // tests cases for enforceForOrderingRelations option:
+        "if (! a < b) {}",
+        "while (! a > b) {}",
+        "foo = ! a <= b;",
+        "foo = ! a >= b;",
+        {
+            code: "! a <= b",
+            options: [{}]
+        },
+        {
+            code: "foo = ! a >= b;",
+            options: [{ enforceForOrderingRelations: false }]
+        },
+        {
+            code: "foo = (!a) >= b;",
+            options: [{ enforceForOrderingRelations: true }]
+        },
+        {
+            code: "a <= b",
+            options: [{ enforceForOrderingRelations: true }]
+        },
+        {
+            code: "!(a < b)",
+            options: [{ enforceForOrderingRelations: true }]
+        },
+        {
+            code: "foo = a > b;",
+            options: [{ enforceForOrderingRelations: true }]
+        }
     ],
     invalid: [
         {
@@ -55,6 +104,31 @@ ruleTester.run("no-unsafe-negation", rule, {
         {
             code: "!(a) instanceof b",
             errors: [unexpectedInstanceofError]
+        },
+        {
+            code: "if (! a < b) {}",
+            options: [{ enforceForOrderingRelations: true }],
+            errors: [unexpectedLessThanOperatorError]
+        },
+        {
+            code: "while (! a > b) {}",
+            options: [{ enforceForOrderingRelations: true }],
+            errors: [unexpectedMoreThanOperatorError]
+        },
+        {
+            code: "foo = ! a <= b;",
+            options: [{ enforceForOrderingRelations: true }],
+            errors: [unexpectedLessThanOrEqualOperatorError]
+        },
+        {
+            code: "foo = ! a >= b;",
+            options: [{ enforceForOrderingRelations: true }],
+            errors: [unexpectedMoreThanOrEqualOperatorError]
+        },
+        {
+            code: "! a <= b",
+            options: [{ enforceForOrderingRelations: true }],
+            errors: [unexpectedLessThanOrEqualOperatorError]
         }
     ]
 });
diff --git a/tests/lib/rules/prefer-numeric-literals.js b/tests/lib/rules/prefer-numeric-literals.js
index 800047051..ddd6e36c8 100644
--- a/tests/lib/rules/prefer-numeric-literals.js
+++ b/tests/lib/rules/prefer-numeric-literals.js
@@ -92,6 +92,104 @@ ruleTester.run("prefer-numeric-literals", rule, {
             errors: [{ message: "Use hexadecimal literals instead of Number.parseInt()." }]
         },
 
+        // Adjacent tokens tests
+        {
+            code: "parseInt('11', 2)",
+            output: "0b11",
+            errors: [{ message: "Use binary literals instead of parseInt()." }]
+        },
+        {
+            code: "Number.parseInt('67', 8)",
+            output: "0o67",
+            errors: [{ message: "Use octal literals instead of Number.parseInt()." }]
+        },
+        {
+            code: "5+parseInt('A', 16)",
+            output: "5+0xA",
+            errors: [{ message: "Use hexadecimal literals instead of parseInt()." }]
+        },
+        {
+            code: "function *f(){ yield(Number).parseInt('11', 2) }",
+            output: "function *f(){ yield 0b11 }",
+            parserOptions: { ecmaVersion: 6 },
+            errors: [{ message: "Use binary literals instead of (Number).parseInt()." }]
+        },
+        {
+            code: "function *f(){ yield(Number.parseInt)('67', 8) }",
+            output: "function *f(){ yield 0o67 }",
+            parserOptions: { ecmaVersion: 6 },
+            errors: [{ message: "Use octal literals instead of Number.parseInt()." }]
+        },
+        {
+            code: "function *f(){ yield(parseInt)('A', 16) }",
+            output: "function *f(){ yield 0xA }",
+            parserOptions: { ecmaVersion: 6 },
+            errors: [{ message: "Use hexadecimal literals instead of parseInt()." }]
+        },
+        {
+            code: "function *f(){ yield Number.parseInt('11', 2) }",
+            output: "function *f(){ yield 0b11 }",
+            parserOptions: { ecmaVersion: 6 },
+            errors: [{ message: "Use binary literals instead of Number.parseInt()." }]
+        },
+        {
+            code: "function *f(){ yield/**/Number.parseInt('67', 8) }",
+            output: "function *f(){ yield/**/0o67 }",
+            parserOptions: { ecmaVersion: 6 },
+            errors: [{ message: "Use octal literals instead of Number.parseInt()." }]
+        },
+        {
+            code: "function *f(){ yield(parseInt('A', 16)) }",
+            output: "function *f(){ yield(0xA) }",
+            parserOptions: { ecmaVersion: 6 },
+            errors: [{ message: "Use hexadecimal literals instead of parseInt()." }]
+        },
+        {
+            code: "parseInt('11', 2)+5",
+            output: "0b11+5",
+            errors: [{ message: "Use binary literals instead of parseInt()." }]
+        },
+        {
+            code: "Number.parseInt('17', 8)+5",
+            output: "0o17+5",
+            errors: [{ message: "Use octal literals instead of Number.parseInt()." }]
+        },
+        {
+            code: "parseInt('A', 16)+5",
+            output: "0xA+5",
+            errors: [{ message: "Use hexadecimal literals instead of parseInt()." }]
+        },
+        {
+            code: "parseInt('11', 2)in foo",
+            output: "0b11 in foo",
+            errors: [{ message: "Use binary literals instead of parseInt()." }]
+        },
+        {
+            code: "Number.parseInt('17', 8)in foo",
+            output: "0o17 in foo",
+            errors: [{ message: "Use octal literals instead of Number.parseInt()." }]
+        },
+        {
+            code: "parseInt('A', 16)in foo",
+            output: "0xA in foo",
+            errors: [{ message: "Use hexadecimal literals instead of parseInt()." }]
+        },
+        {
+            code: "parseInt('11', 2) in foo",
+            output: "0b11 in foo",
+            errors: [{ message: "Use binary literals instead of parseInt()." }]
+        },
+        {
+            code: "Number.parseInt('17', 8)/**/in foo",
+            output: "0o17/**/in foo",
+            errors: [{ message: "Use octal literals instead of Number.parseInt()." }]
+        },
+        {
+            code: "(parseInt('A', 16))in foo",
+            output: "(0xA)in foo",
+            errors: [{ message: "Use hexadecimal literals instead of parseInt()." }]
+        },
+
         // Should not autofix if it would remove comments
         {
             code: "/* comment */Number.parseInt('11', 2);",
diff --git a/tests/lib/source-code/source-code.js b/tests/lib/source-code/source-code.js
index 57cc960d2..9e8da4844 100644
--- a/tests/lib/source-code/source-code.js
+++ b/tests/lib/source-code/source-code.js
@@ -1796,6 +1796,7 @@ describe("SourceCode", () => {
             ["let  foo = bar;", true],
             ["let /**/ foo = bar;", true],
             ["let/**/foo = bar;", false],
+            ["let/*\n*/foo = bar", false],
             ["a+b", false],
             ["a/**/+b", false],
             ["a/* */+b", false],