From 97b57ae3ebae9150456f5516c64b6d2ba75b4038 Mon Sep 17 00:00:00 2001 From: Milos Djermanovic Date: Wed, 6 Apr 2022 02:31:34 +0200 Subject: [PATCH] fix: invalid operator in operator-assignment messages (#15759) --- lib/rules/operator-assignment.js | 12 ++- tests/lib/rules/operator-assignment.js | 141 ++++++++++++------------- 2 files changed, 76 insertions(+), 77 deletions(-) diff --git a/lib/rules/operator-assignment.js b/lib/rules/operator-assignment.js index d200811634c..50cdff9f9a0 100644 --- a/lib/rules/operator-assignment.js +++ b/lib/rules/operator-assignment.js @@ -76,8 +76,8 @@ module.exports = { fixable: "code", messages: { - replaced: "Assignment (=) can be replaced with operator assignment ({{operator}}=).", - unexpected: "Unexpected operator assignment ({{operator}}=) shorthand." + replaced: "Assignment (=) can be replaced with operator assignment ({{operator}}).", + unexpected: "Unexpected operator assignment ({{operator}}) shorthand." } }, @@ -109,11 +109,13 @@ module.exports = { const operator = expr.operator; if (isCommutativeOperatorWithShorthand(operator) || isNonCommutativeOperatorWithShorthand(operator)) { + const replacementOperator = `${operator}=`; + if (astUtils.isSameReference(left, expr.left, true)) { context.report({ node, messageId: "replaced", - data: { operator }, + data: { operator: replacementOperator }, fix(fixer) { if (canBeFixed(left) && canBeFixed(expr.left)) { const equalsToken = getOperatorToken(node); @@ -126,7 +128,7 @@ module.exports = { return null; } - return fixer.replaceText(node, `${leftText}${expr.operator}=${rightText}`); + return fixer.replaceText(node, `${leftText}${replacementOperator}${rightText}`); } return null; } @@ -141,7 +143,7 @@ module.exports = { context.report({ node, messageId: "replaced", - data: { operator } + data: { operator: replacementOperator } }); } } diff --git a/tests/lib/rules/operator-assignment.js b/tests/lib/rules/operator-assignment.js index 8f814840e29..50229af4903 100644 --- a/tests/lib/rules/operator-assignment.js +++ b/tests/lib/rules/operator-assignment.js @@ -18,9 +18,6 @@ const rule = require("../../../lib/rules/operator-assignment"), const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 2022 } }); -const EXPECTED_OPERATOR_ASSIGNMENT = [{ messageId: "replaced", type: "AssignmentExpression" }]; -const UNEXPECTED_OPERATOR_ASSIGNMENT = [{ messageId: "unexpected", type: "AssignmentExpression" }]; - ruleTester.run("operator-assignment", rule, { valid: [ @@ -117,326 +114,326 @@ ruleTester.run("operator-assignment", rule, { invalid: [{ code: "x = x + y", output: "x += y", - errors: EXPECTED_OPERATOR_ASSIGNMENT + errors: [{ messageId: "replaced", type: "AssignmentExpression", data: { operator: "+=" } }] }, { code: "x = x - y", output: "x -= y", - errors: EXPECTED_OPERATOR_ASSIGNMENT + errors: [{ messageId: "replaced", type: "AssignmentExpression", data: { operator: "-=" } }] }, { code: "x = x * y", output: "x *= y", - errors: EXPECTED_OPERATOR_ASSIGNMENT + errors: [{ messageId: "replaced", type: "AssignmentExpression", data: { operator: "*=" } }] }, { code: "x = y * x", output: null, // not fixed (possible change in behavior if y and x have valueOf() functions) - errors: EXPECTED_OPERATOR_ASSIGNMENT + errors: [{ messageId: "replaced", type: "AssignmentExpression", data: { operator: "*=" } }] }, { code: "x = (y * z) * x", output: null, // not fixed (possible change in behavior if y/z and x have valueOf() functions) - errors: EXPECTED_OPERATOR_ASSIGNMENT + errors: [{ messageId: "replaced", type: "AssignmentExpression", data: { operator: "*=" } }] }, { code: "x = x / y", output: "x /= y", - errors: EXPECTED_OPERATOR_ASSIGNMENT + errors: [{ messageId: "replaced", type: "AssignmentExpression", data: { operator: "/=" } }] }, { code: "x = x % y", output: "x %= y", - errors: EXPECTED_OPERATOR_ASSIGNMENT + errors: [{ messageId: "replaced", type: "AssignmentExpression", data: { operator: "%=" } }] }, { code: "x = x << y", output: "x <<= y", - errors: EXPECTED_OPERATOR_ASSIGNMENT + errors: [{ messageId: "replaced", type: "AssignmentExpression", data: { operator: "<<=" } }] }, { code: "x = x >> y", output: "x >>= y", - errors: EXPECTED_OPERATOR_ASSIGNMENT + errors: [{ messageId: "replaced", type: "AssignmentExpression", data: { operator: ">>=" } }] }, { code: "x = x >>> y", output: "x >>>= y", - errors: EXPECTED_OPERATOR_ASSIGNMENT + errors: [{ messageId: "replaced", type: "AssignmentExpression", data: { operator: ">>>=" } }] }, { code: "x = x & y", output: "x &= y", - errors: EXPECTED_OPERATOR_ASSIGNMENT + errors: [{ messageId: "replaced", type: "AssignmentExpression", data: { operator: "&=" } }] }, { code: "x = x ^ y", output: "x ^= y", - errors: EXPECTED_OPERATOR_ASSIGNMENT + errors: [{ messageId: "replaced", type: "AssignmentExpression", data: { operator: "^=" } }] }, { code: "x = x | y", output: "x |= y", - errors: EXPECTED_OPERATOR_ASSIGNMENT + errors: [{ messageId: "replaced", type: "AssignmentExpression", data: { operator: "|=" } }] }, { code: "x[0] = x[0] - y", output: "x[0] -= y", - errors: EXPECTED_OPERATOR_ASSIGNMENT + errors: [{ messageId: "replaced", type: "AssignmentExpression", data: { operator: "-=" } }] }, { code: "x.y[z['a']][0].b = x.y[z['a']][0].b * 2", output: null, // not fixed; might activate getters more than before - errors: EXPECTED_OPERATOR_ASSIGNMENT + errors: [{ messageId: "replaced", type: "AssignmentExpression", data: { operator: "*=" } }] }, { code: "x = x + y", output: "x += y", options: ["always"], - errors: EXPECTED_OPERATOR_ASSIGNMENT + errors: [{ messageId: "replaced", type: "AssignmentExpression", data: { operator: "+=" } }] }, { code: "x = (x + y)", output: "x += y", options: ["always"], - errors: EXPECTED_OPERATOR_ASSIGNMENT + errors: [{ messageId: "replaced", type: "AssignmentExpression", data: { operator: "+=" } }] }, { code: "x = x + (y)", output: "x += (y)", options: ["always"], - errors: EXPECTED_OPERATOR_ASSIGNMENT + errors: [{ messageId: "replaced", type: "AssignmentExpression", data: { operator: "+=" } }] }, { code: "x += (y)", output: "x = x + (y)", options: ["never"], - errors: UNEXPECTED_OPERATOR_ASSIGNMENT + errors: [{ messageId: "unexpected", type: "AssignmentExpression", data: { operator: "+=" } }] }, { code: "x += y", output: "x = x + y", options: ["never"], - errors: UNEXPECTED_OPERATOR_ASSIGNMENT + errors: [{ messageId: "unexpected", type: "AssignmentExpression", data: { operator: "+=" } }] }, { code: "foo.bar = foo.bar + baz", output: "foo.bar += baz", - errors: EXPECTED_OPERATOR_ASSIGNMENT + errors: [{ messageId: "replaced", type: "AssignmentExpression", data: { operator: "+=" } }] }, { code: "foo.bar += baz", output: "foo.bar = foo.bar + baz", options: ["never"], - errors: UNEXPECTED_OPERATOR_ASSIGNMENT + errors: [{ messageId: "unexpected", type: "AssignmentExpression", data: { operator: "+=" } }] }, { code: "this.foo = this.foo + bar", output: "this.foo += bar", - errors: EXPECTED_OPERATOR_ASSIGNMENT + errors: [{ messageId: "replaced", type: "AssignmentExpression", data: { operator: "+=" } }] }, { code: "this.foo += bar", output: "this.foo = this.foo + bar", options: ["never"], - errors: UNEXPECTED_OPERATOR_ASSIGNMENT + errors: [{ messageId: "unexpected", type: "AssignmentExpression", data: { operator: "+=" } }] }, { code: "foo.bar.baz = foo.bar.baz + qux", output: null, // not fixed; fixing would cause a foo.bar getter to activate once rather than twice - errors: EXPECTED_OPERATOR_ASSIGNMENT + errors: [{ messageId: "replaced", type: "AssignmentExpression", data: { operator: "+=" } }] }, { code: "foo.bar.baz += qux", output: null, // not fixed; fixing would cause a foo.bar getter to activate twice rather than once options: ["never"], - errors: UNEXPECTED_OPERATOR_ASSIGNMENT + errors: [{ messageId: "unexpected", type: "AssignmentExpression", data: { operator: "+=" } }] }, { code: "this.foo.bar = this.foo.bar + baz", output: null, // not fixed; fixing would cause a this.foo getter to activate once rather than twice - errors: EXPECTED_OPERATOR_ASSIGNMENT + errors: [{ messageId: "replaced", type: "AssignmentExpression", data: { operator: "+=" } }] }, { code: "this.foo.bar += baz", output: null, // not fixed; fixing would cause a this.foo getter to activate twice rather than once options: ["never"], - errors: UNEXPECTED_OPERATOR_ASSIGNMENT + errors: [{ messageId: "unexpected", type: "AssignmentExpression", data: { operator: "+=" } }] }, { code: "foo[bar] = foo[bar] + baz", output: null, // not fixed; fixing would cause bar.toString() to get called once instead of twice - errors: EXPECTED_OPERATOR_ASSIGNMENT + errors: [{ messageId: "replaced", type: "AssignmentExpression", data: { operator: "+=" } }] }, { code: "this[foo] = this[foo] + bar", output: null, // not fixed; fixing would cause foo.toString() to get called once instead of twice - errors: EXPECTED_OPERATOR_ASSIGNMENT + errors: [{ messageId: "replaced", type: "AssignmentExpression", data: { operator: "+=" } }] }, { code: "foo[bar] >>>= baz", output: null, // not fixed; fixing would cause bar.toString() to get called twice instead of once options: ["never"], - errors: UNEXPECTED_OPERATOR_ASSIGNMENT + errors: [{ messageId: "unexpected", type: "AssignmentExpression", data: { operator: ">>>=" } }] }, { code: "this[foo] >>>= bar", output: null, // not fixed; fixing would cause foo.toString() to get called twice instead of once options: ["never"], - errors: UNEXPECTED_OPERATOR_ASSIGNMENT + errors: [{ messageId: "unexpected", type: "AssignmentExpression", data: { operator: ">>>=" } }] }, { code: "foo[5] = foo[5] / baz", output: "foo[5] /= baz", // this is ok because 5 is a literal, so toString won't get called - errors: EXPECTED_OPERATOR_ASSIGNMENT + errors: [{ messageId: "replaced", type: "AssignmentExpression", data: { operator: "/=" } }] }, { code: "this[5] = this[5] / foo", output: "this[5] /= foo", // this is ok because 5 is a literal, so toString won't get called - errors: EXPECTED_OPERATOR_ASSIGNMENT + errors: [{ messageId: "replaced", type: "AssignmentExpression", data: { operator: "/=" } }] }, { code: "/*1*/x/*2*/./*3*/y/*4*/= x.y +/*5*/z/*6*/./*7*/w/*8*/;", output: "/*1*/x/*2*/./*3*/y/*4*/+=/*5*/z/*6*/./*7*/w/*8*/;", // these comments are preserved options: ["always"], - errors: EXPECTED_OPERATOR_ASSIGNMENT + errors: [{ messageId: "replaced", type: "AssignmentExpression", data: { operator: "+=" } }] }, { code: "x // 1\n . // 2\n y // 3\n = x.y + //4\n z //5\n . //6\n w;", output: "x // 1\n . // 2\n y // 3\n += //4\n z //5\n . //6\n w;", // these comments are preserved options: ["always"], - errors: EXPECTED_OPERATOR_ASSIGNMENT + errors: [{ messageId: "replaced", type: "AssignmentExpression", data: { operator: "+=" } }] }, { code: "x = /*1*/ x + y", output: null, // not fixed; fixing would remove this comment options: ["always"], - errors: EXPECTED_OPERATOR_ASSIGNMENT + errors: [{ messageId: "replaced", type: "AssignmentExpression", data: { operator: "+=" } }] }, { code: "x = //1\n x + y", output: null, // not fixed; fixing would remove this comment options: ["always"], - errors: EXPECTED_OPERATOR_ASSIGNMENT + errors: [{ messageId: "replaced", type: "AssignmentExpression", data: { operator: "+=" } }] }, { code: "x.y = x/*1*/.y + z", output: null, // not fixed; fixing would remove this comment options: ["always"], - errors: EXPECTED_OPERATOR_ASSIGNMENT + errors: [{ messageId: "replaced", type: "AssignmentExpression", data: { operator: "+=" } }] }, { code: "x.y = x. //1\n y + z", output: null, // not fixed; fixing would remove this comment options: ["always"], - errors: EXPECTED_OPERATOR_ASSIGNMENT + errors: [{ messageId: "replaced", type: "AssignmentExpression", data: { operator: "+=" } }] }, { code: "x = x /*1*/ + y", output: null, // not fixed; fixing would remove this comment options: ["always"], - errors: EXPECTED_OPERATOR_ASSIGNMENT + errors: [{ messageId: "replaced", type: "AssignmentExpression", data: { operator: "+=" } }] }, { code: "x = x //1\n + y", output: null, // not fixed; fixing would remove this comment options: ["always"], - errors: EXPECTED_OPERATOR_ASSIGNMENT + errors: [{ messageId: "replaced", type: "AssignmentExpression", data: { operator: "+=" } }] }, { code: "/*1*/x +=/*2*/y/*3*/;", output: "/*1*/x = x +/*2*/y/*3*/;", // these comments are preserved and not duplicated options: ["never"], - errors: UNEXPECTED_OPERATOR_ASSIGNMENT + errors: [{ messageId: "unexpected", type: "AssignmentExpression", data: { operator: "+=" } }] }, { code: "x +=//1\n y", output: "x = x +//1\n y", // this comment is preserved and not duplicated options: ["never"], - errors: UNEXPECTED_OPERATOR_ASSIGNMENT + errors: [{ messageId: "unexpected", type: "AssignmentExpression", data: { operator: "+=" } }] }, { code: "(/*1*/x += y)", output: "(/*1*/x = x + y)", // this comment is preserved and not duplicated options: ["never"], - errors: UNEXPECTED_OPERATOR_ASSIGNMENT + errors: [{ messageId: "unexpected", type: "AssignmentExpression", data: { operator: "+=" } }] }, { code: "x/*1*/+= y", output: null, // not fixed; fixing would duplicate this comment options: ["never"], - errors: UNEXPECTED_OPERATOR_ASSIGNMENT + errors: [{ messageId: "unexpected", type: "AssignmentExpression", data: { operator: "+=" } }] }, { code: "x //1\n += y", output: null, // not fixed; fixing would duplicate this comment options: ["never"], - errors: UNEXPECTED_OPERATOR_ASSIGNMENT + errors: [{ messageId: "unexpected", type: "AssignmentExpression", data: { operator: "+=" } }] }, { code: "(/*1*/x) += y", output: null, // not fixed; fixing would duplicate this comment options: ["never"], - errors: UNEXPECTED_OPERATOR_ASSIGNMENT + errors: [{ messageId: "unexpected", type: "AssignmentExpression", data: { operator: "+=" } }] }, { code: "x/*1*/.y += z", output: null, // not fixed; fixing would duplicate this comment options: ["never"], - errors: UNEXPECTED_OPERATOR_ASSIGNMENT + errors: [{ messageId: "unexpected", type: "AssignmentExpression", data: { operator: "+=" } }] }, { code: "x.//1\n y += z", output: null, // not fixed; fixing would duplicate this comment options: ["never"], - errors: UNEXPECTED_OPERATOR_ASSIGNMENT + errors: [{ messageId: "unexpected", type: "AssignmentExpression", data: { operator: "+=" } }] }, { code: "(foo.bar) ^= ((((((((((((((((baz))))))))))))))))", output: "(foo.bar) = (foo.bar) ^ ((((((((((((((((baz))))))))))))))))", options: ["never"], - errors: UNEXPECTED_OPERATOR_ASSIGNMENT + errors: [{ messageId: "unexpected", type: "AssignmentExpression", data: { operator: "^=" } }] }, { code: "foo = foo ** bar", output: "foo **= bar", - errors: EXPECTED_OPERATOR_ASSIGNMENT + errors: [{ messageId: "replaced", type: "AssignmentExpression", data: { operator: "**=" } }] }, { code: "foo **= bar", output: "foo = foo ** bar", options: ["never"], - errors: UNEXPECTED_OPERATOR_ASSIGNMENT + errors: [{ messageId: "unexpected", type: "AssignmentExpression", data: { operator: "**=" } }] }, { code: "foo *= bar + 1", output: "foo = foo * (bar + 1)", options: ["never"], - errors: UNEXPECTED_OPERATOR_ASSIGNMENT + errors: [{ messageId: "unexpected", type: "AssignmentExpression", data: { operator: "*=" } }] }, { code: "foo -= bar - baz", output: "foo = foo - (bar - baz)", options: ["never"], - errors: UNEXPECTED_OPERATOR_ASSIGNMENT + errors: [{ messageId: "unexpected", type: "AssignmentExpression", data: { operator: "-=" } }] }, { code: "foo += bar + baz", output: "foo = foo + (bar + baz)", // addition is not associative in JS, e.g. (1 + 2) + '3' !== 1 + (2 + '3') options: ["never"], - errors: UNEXPECTED_OPERATOR_ASSIGNMENT + errors: [{ messageId: "unexpected", type: "AssignmentExpression", data: { operator: "+=" } }] }, { code: "foo += bar = 1", output: "foo = foo + (bar = 1)", options: ["never"], - errors: UNEXPECTED_OPERATOR_ASSIGNMENT + errors: [{ messageId: "unexpected", type: "AssignmentExpression", data: { operator: "+=" } }] }, { code: "foo *= (bar + 1)", output: "foo = foo * (bar + 1)", options: ["never"], - errors: UNEXPECTED_OPERATOR_ASSIGNMENT + errors: [{ messageId: "unexpected", type: "AssignmentExpression", data: { operator: "*=" } }] }, { code: "foo+=-bar", output: "foo= foo+-bar", // tokens can be adjacent options: ["never"], - errors: UNEXPECTED_OPERATOR_ASSIGNMENT + errors: [{ messageId: "unexpected", type: "AssignmentExpression", data: { operator: "+=" } }] }, { code: "foo/=bar", output: "foo= foo/bar", // tokens can be adjacent options: ["never"], - errors: UNEXPECTED_OPERATOR_ASSIGNMENT + errors: [{ messageId: "unexpected", type: "AssignmentExpression", data: { operator: "/=" } }] }, { code: "foo/=/**/bar", output: "foo= foo/ /**/bar", // // tokens cannot be adjacent, insert a space between options: ["never"], - errors: UNEXPECTED_OPERATOR_ASSIGNMENT + errors: [{ messageId: "unexpected", type: "AssignmentExpression", data: { operator: "/=" } }] }, { code: "foo/=//\nbar", output: "foo= foo/ //\nbar", // // tokens cannot be adjacent, insert a space between options: ["never"], - errors: UNEXPECTED_OPERATOR_ASSIGNMENT + errors: [{ messageId: "unexpected", type: "AssignmentExpression", data: { operator: "/=" } }] }, { code: "foo/=/^bar$/", output: "foo= foo/ /^bar$/", // // tokens cannot be adjacent, insert a space between options: ["never"], - errors: UNEXPECTED_OPERATOR_ASSIGNMENT + errors: [{ messageId: "unexpected", type: "AssignmentExpression", data: { operator: "/=" } }] }, { code: "foo+=+bar", output: "foo= foo+ +bar", // tokens cannot be adjacent, insert a space between options: ["never"], - errors: UNEXPECTED_OPERATOR_ASSIGNMENT + errors: [{ messageId: "unexpected", type: "AssignmentExpression", data: { operator: "+=" } }] }, { code: "foo+= +bar", output: "foo= foo+ +bar", // tokens cannot be adjacent, but there is already a space between options: ["never"], - errors: UNEXPECTED_OPERATOR_ASSIGNMENT + errors: [{ messageId: "unexpected", type: "AssignmentExpression", data: { operator: "+=" } }] }, { code: "foo+=/**/+bar", output: "foo= foo+/**/+bar", // tokens cannot be adjacent, but there is a comment between options: ["never"], - errors: UNEXPECTED_OPERATOR_ASSIGNMENT + errors: [{ messageId: "unexpected", type: "AssignmentExpression", data: { operator: "+=" } }] }, { code: "foo+=+bar===baz", output: "foo= foo+(+bar===baz)", // tokens cannot be adjacent, but the right side will be parenthesised options: ["never"], - errors: UNEXPECTED_OPERATOR_ASSIGNMENT + errors: [{ messageId: "unexpected", type: "AssignmentExpression", data: { operator: "+=" } }] }, // Optional chaining { code: "(obj?.a).b = (obj?.a).b + y", output: null, - errors: EXPECTED_OPERATOR_ASSIGNMENT + errors: [{ messageId: "replaced", type: "AssignmentExpression", data: { operator: "+=" } }] }, { code: "obj.a = obj?.a + b", output: null, - errors: EXPECTED_OPERATOR_ASSIGNMENT + errors: [{ messageId: "replaced", type: "AssignmentExpression", data: { operator: "+=" } }] } ]