From 44c6fc879de61e9513835d1d4d6ae978d9a43c51 Mon Sep 17 00:00:00 2001 From: Milos Djermanovic Date: Tue, 24 Aug 2021 18:05:16 +0200 Subject: [PATCH] Update: support class fields in func-name-matching (refs #14857) (#14964) --- docs/rules/func-name-matching.md | 38 +++ lib/rules/func-name-matching.js | 14 +- tests/lib/rules/func-name-matching.js | 324 ++++++++++++++++++++++++++ 3 files changed, 371 insertions(+), 5 deletions(-) diff --git a/docs/rules/func-name-matching.md b/docs/rules/func-name-matching.md index 53be2a09987..39ee411f035 100644 --- a/docs/rules/func-name-matching.md +++ b/docs/rules/func-name-matching.md @@ -15,6 +15,10 @@ obj.foo = function bar() {}; obj['foo'] = function bar() {}; var obj = {foo: function bar() {}}; ({['foo']: function bar() {}}); + +class C { + foo = function bar() {}; +} ``` ```js @@ -26,6 +30,10 @@ obj.foo = function foo() {}; obj['foo'] = function foo() {}; var obj = {foo: function foo() {}}; ({['foo']: function foo() {}}); + +class C { + foo = function foo() {}; +} ``` Examples of **correct** code for this rule: @@ -54,6 +62,21 @@ obj['x' + 2] = function bar(){}; var [ bar ] = [ function bar(){} ]; ({[foo]: function bar() {}}) +class C { + foo = function foo() {}; + baz = function() {}; +} + +// private names are ignored +class D { + #foo = function foo() {}; + #bar = function foo() {}; + baz() { + this.#foo = function foo() {}; + this.#foo = function bar() {}; + } +} + module.exports = function foo(name) {}; module['exports'] = function foo(name) {}; ``` @@ -81,6 +104,21 @@ obj['x' + 2] = function bar(){}; var [ bar ] = [ function bar(){} ]; ({[foo]: function bar() {}}) +class C { + foo = function bar() {}; + baz = function() {}; +} + +// private names are ignored +class D { + #foo = function foo() {}; + #bar = function foo() {}; + baz() { + this.#foo = function foo() {}; + this.#foo = function bar() {}; + } +} + module.exports = function foo(name) {}; module['exports'] = function foo(name) {}; ``` diff --git a/lib/rules/func-name-matching.js b/lib/rules/func-name-matching.js index c15aa4a3423..122cfd8d33c 100644 --- a/lib/rules/func-name-matching.js +++ b/lib/rules/func-name-matching.js @@ -195,21 +195,25 @@ module.exports = { const isProp = node.left.type === "MemberExpression"; const name = isProp ? astUtils.getStaticPropertyName(node.left) : node.left.name; - if (node.right.id && isIdentifier(name) && shouldWarn(name, node.right.id.name)) { + if (node.right.id && name && isIdentifier(name) && shouldWarn(name, node.right.id.name)) { report(node, name, node.right.id.name, isProp); } }, - Property(node) { - if (node.value.type !== "FunctionExpression" || !node.value.id || node.computed && !isStringLiteral(node.key)) { + "Property, PropertyDefinition[value]"(node) { + if (!(node.value.type === "FunctionExpression" && node.value.id)) { return; } - if (node.key.type === "Identifier") { + if (node.key.type === "Identifier" && !node.computed) { const functionName = node.value.id.name; let propertyName = node.key.name; - if (considerPropertyDescriptor && propertyName === "value") { + if ( + considerPropertyDescriptor && + propertyName === "value" && + node.parent.type === "ObjectExpression" + ) { if (isPropertyCall("Object", "defineProperty", node.parent.parent) || isPropertyCall("Reflect", "defineProperty", node.parent.parent)) { const property = node.parent.parent.arguments[1]; diff --git a/tests/lib/rules/func-name-matching.js b/tests/lib/rules/func-name-matching.js index 908e9108f3e..7e215254410 100644 --- a/tests/lib/rules/func-name-matching.js +++ b/tests/lib/rules/func-name-matching.js @@ -262,6 +262,248 @@ ruleTester.run("func-name-matching", rule, { { code: "foo({ value: function value() {} })", options: ["always", { considerPropertyDescriptor: true }] + }, + + // class fields, private names are ignored + { + code: "class C { x = function () {}; }", + options: ["always"], + parserOptions: { ecmaVersion: 2022 } + }, + { + code: "class C { x = function () {}; }", + options: ["never"], + parserOptions: { ecmaVersion: 2022 } + }, + { + code: "class C { 'x' = function () {}; }", + options: ["always"], + parserOptions: { ecmaVersion: 2022 } + }, + { + code: "class C { 'x' = function () {}; }", + options: ["never"], + parserOptions: { ecmaVersion: 2022 } + }, + { + code: "class C { #x = function () {}; }", + options: ["always"], + parserOptions: { ecmaVersion: 2022 } + }, + { + code: "class C { #x = function () {}; }", + options: ["never"], + parserOptions: { ecmaVersion: 2022 } + }, + { + code: "class C { [x] = function () {}; }", + options: ["always"], + parserOptions: { ecmaVersion: 2022 } + }, + { + code: "class C { [x] = function () {}; }", + options: ["never"], + parserOptions: { ecmaVersion: 2022 } + }, + { + code: "class C { ['x'] = function () {}; }", + options: ["always"], + parserOptions: { ecmaVersion: 2022 } + }, + { + code: "class C { ['x'] = function () {}; }", + options: ["never"], + parserOptions: { ecmaVersion: 2022 } + }, + { + code: "class C { x = function x() {}; }", + options: ["always"], + parserOptions: { ecmaVersion: 2022 } + }, + { + code: "class C { x = function y() {}; }", + options: ["never"], + parserOptions: { ecmaVersion: 2022 } + }, + { + code: "class C { 'x' = function x() {}; }", + options: ["always"], + parserOptions: { ecmaVersion: 2022 } + }, + { + code: "class C { 'x' = function y() {}; }", + options: ["never"], + parserOptions: { ecmaVersion: 2022 } + }, + { + code: "class C { #x = function x() {}; }", + options: ["always"], + parserOptions: { ecmaVersion: 2022 } + }, + { + code: "class C { #x = function x() {}; }", + options: ["never"], + parserOptions: { ecmaVersion: 2022 } + }, + { + code: "class C { #x = function y() {}; }", + options: ["always"], + parserOptions: { ecmaVersion: 2022 } + }, + { + code: "class C { #x = function y() {}; }", + options: ["never"], + parserOptions: { ecmaVersion: 2022 } + }, + { + code: "class C { [x] = function x() {}; }", + options: ["always"], + parserOptions: { ecmaVersion: 2022 } + }, + { + code: "class C { [x] = function x() {}; }", + options: ["never"], + parserOptions: { ecmaVersion: 2022 } + }, + { + code: "class C { [x] = function y() {}; }", + options: ["always"], + parserOptions: { ecmaVersion: 2022 } + }, + { + code: "class C { [x] = function y() {}; }", + options: ["never"], + parserOptions: { ecmaVersion: 2022 } + }, + { + code: "class C { ['x'] = function x() {}; }", + options: ["always"], + parserOptions: { ecmaVersion: 2022 } + }, + { + code: "class C { ['x'] = function y() {}; }", + options: ["never"], + parserOptions: { ecmaVersion: 2022 } + }, + { + code: "class C { 'xy ' = function foo() {}; }", + options: ["always"], + parserOptions: { ecmaVersion: 2022 } + }, + { + code: "class C { 'xy ' = function xy() {}; }", + options: ["never"], + parserOptions: { ecmaVersion: 2022 } + }, + { + code: "class C { ['xy '] = function foo() {}; }", + options: ["always"], + parserOptions: { ecmaVersion: 2022 } + }, + { + code: "class C { ['xy '] = function xy() {}; }", + options: ["never"], + parserOptions: { ecmaVersion: 2022 } + }, + { + code: "class C { 1 = function x0() {}; }", + options: ["always"], + parserOptions: { ecmaVersion: 2022 } + }, + { + code: "class C { 1 = function x1() {}; }", + options: ["never"], + parserOptions: { ecmaVersion: 2022 } + }, + { + code: "class C { [1] = function x0() {}; }", + options: ["always"], + parserOptions: { ecmaVersion: 2022 } + }, + { + code: "class C { [1] = function x1() {}; }", + options: ["never"], + parserOptions: { ecmaVersion: 2022 } + }, + { + code: "class C { [f()] = function g() {}; }", + options: ["always"], + parserOptions: { ecmaVersion: 2022 } + }, + { + code: "class C { [f()] = function f() {}; }", + options: ["never"], + parserOptions: { ecmaVersion: 2022 } + }, + { + code: "class C { static x = function x() {}; }", + options: ["always"], + parserOptions: { ecmaVersion: 2022 } + }, + { + code: "class C { static x = function y() {}; }", + options: ["never"], + parserOptions: { ecmaVersion: 2022 } + }, + { + code: "class C { x = (function y() {})(); }", + options: ["always"], + parserOptions: { ecmaVersion: 2022 } + }, + { + code: "class C { x = (function x() {})(); }", + options: ["never"], + parserOptions: { ecmaVersion: 2022 } + }, + { + code: "(class { x = function x() {}; })", + options: ["always"], + parserOptions: { ecmaVersion: 2022 } + }, + { + code: "(class { x = function y() {}; })", + options: ["never"], + parserOptions: { ecmaVersion: 2022 } + }, + { + code: "class C { #x; foo() { this.#x = function x() {}; } }", + options: ["always"], + parserOptions: { ecmaVersion: 2022 } + }, + { + code: "class C { #x; foo() { this.#x = function x() {}; } }", + options: ["never"], + parserOptions: { ecmaVersion: 2022 } + }, + { + code: "class C { #x; foo() { this.#x = function y() {}; } }", + options: ["always"], + parserOptions: { ecmaVersion: 2022 } + }, + { + code: "class C { #x; foo() { this.#x = function y() {}; } }", + options: ["never"], + parserOptions: { ecmaVersion: 2022 } + }, + { + code: "class C { #x; foo() { a.b.#x = function x() {}; } }", + options: ["always"], + parserOptions: { ecmaVersion: 2022 } + }, + { + code: "class C { #x; foo() { a.b.#x = function x() {}; } }", + options: ["never"], + parserOptions: { ecmaVersion: 2022 } + }, + { + code: "class C { #x; foo() { a.b.#x = function y() {}; } }", + options: ["always"], + parserOptions: { ecmaVersion: 2022 } + }, + { + code: "class C { #x; foo() { a.b.#x = function y() {}; } }", + options: ["never"], + parserOptions: { ecmaVersion: 2022 } } ], invalid: [ @@ -554,6 +796,88 @@ ruleTester.run("func-name-matching", rule, { errors: [ { messageId: "notMatchProperty", data: { funcName: "bar", name: "bar" } } ] + }, + + // class fields + { + code: "class C { x = function y() {}; }", + options: ["always"], + parserOptions: { ecmaVersion: 2022 }, + errors: [ + { messageId: "matchProperty", data: { funcName: "y", name: "x" } } + ] + }, + { + code: "class C { x = function x() {}; }", + options: ["never"], + parserOptions: { ecmaVersion: 2022 }, + errors: [ + { messageId: "notMatchProperty", data: { funcName: "x", name: "x" } } + ] + }, + { + code: "class C { 'x' = function y() {}; }", + options: ["always"], + parserOptions: { ecmaVersion: 2022 }, + errors: [ + { messageId: "matchProperty", data: { funcName: "y", name: "x" } } + ] + }, + { + code: "class C { 'x' = function x() {}; }", + options: ["never"], + parserOptions: { ecmaVersion: 2022 }, + errors: [ + { messageId: "notMatchProperty", data: { funcName: "x", name: "x" } } + ] + }, + { + code: "class C { ['x'] = function y() {}; }", + options: ["always"], + parserOptions: { ecmaVersion: 2022 }, + errors: [ + { messageId: "matchProperty", data: { funcName: "y", name: "x" } } + ] + }, + { + code: "class C { ['x'] = function x() {}; }", + options: ["never"], + parserOptions: { ecmaVersion: 2022 }, + errors: [ + { messageId: "notMatchProperty", data: { funcName: "x", name: "x" } } + ] + }, + { + code: "class C { static x = function y() {}; }", + options: ["always"], + parserOptions: { ecmaVersion: 2022 }, + errors: [ + { messageId: "matchProperty", data: { funcName: "y", name: "x" } } + ] + }, + { + code: "class C { static x = function x() {}; }", + options: ["never"], + parserOptions: { ecmaVersion: 2022 }, + errors: [ + { messageId: "notMatchProperty", data: { funcName: "x", name: "x" } } + ] + }, + { + code: "(class { x = function y() {}; })", + options: ["always"], + parserOptions: { ecmaVersion: 2022 }, + errors: [ + { messageId: "matchProperty", data: { funcName: "y", name: "x" } } + ] + }, + { + code: "(class { x = function x() {}; })", + options: ["never"], + parserOptions: { ecmaVersion: 2022 }, + errors: [ + { messageId: "notMatchProperty", data: { funcName: "x", name: "x" } } + ] } ] });