diff --git a/docs/rules/accessor-pairs.md b/docs/rules/accessor-pairs.md index 183bc42b6e6..43a14cc18b6 100644 --- a/docs/rules/accessor-pairs.md +++ b/docs/rules/accessor-pairs.md @@ -1,4 +1,4 @@ -# Enforces getter/setter pairs in objects (accessor-pairs) +# Enforces getter/setter pairs in objects and classes (accessor-pairs) It's a common mistake in JavaScript to create an object with just a setter for a property but never have a corresponding getter defined for it. Without a getter, you cannot read the property, so it ends up not being used. @@ -32,10 +32,14 @@ This rule enforces a style where it requires to have a getter for every property By activating the option `getWithoutSet` it enforces the presence of a setter for every property which has a getter defined. +By default, this rule checks only object literals and property descriptors. If you want this rule +to also check class declarations and class expressions, activate the option `enforceForClassMembers`. + ## Options * `setWithoutGet` set to `true` will warn for setters without getters (Default `true`). * `getWithoutSet` set to `true` will warn for getters without setters (Default `false`). +* `enforceForClassMembers` set to `true` additionally applies this rule to class getters/setters (Default `false`). ### setWithoutGet @@ -143,6 +147,61 @@ Object.defineProperty(o, 'c', { ``` +### enforceForClassMembers + +By default, this rule does not enforce getter/setter pairs in class declarations and class expressions, +as the default value for `enforceForClassMembers` is `false`. + +When `enforceForClassMembers` is set to `true`: + +* `"getWithoutSet": true` will also warn for getters without setters in classes. +* `"setWithoutGet": true` will also warn for setters without getters in classes. + +Examples of **incorrect** code for `{ "getWithoutSet": true, "enforceForClassMembers": true }`: + +```js +/*eslint accessor-pairs: ["error", { "getWithoutSet": true, "enforceForClassMembers": true }]*/ + +class Foo { + get a() { + return this.val; + } +} + +class Bar { + static get a() { + return this.val; + } +} + +const Baz = class { + get a() { + return this.val; + } + static set a(value) { + this.val = value; + } +} +``` + +Examples of **incorrect** code for `{ "setWithoutGet": true, "enforceForClassMembers": true }`: + +```js +/*eslint accessor-pairs: ["error", { "setWithoutGet": true, "enforceForClassMembers": true }]*/ + +class Foo { + set a(value) { + this.val = value; + } +} + +const Bar = class { + static set a(value) { + this.val = value; + } +} +``` + ## Known Limitations Due to the limits of static analysis, this rule does not account for possible side effects and in certain cases @@ -164,7 +223,7 @@ var o = { }; ``` -Also, this rule does not disallow duplicate keys in object literals, and in certain cases with duplicate keys +Also, this rule does not disallow duplicate keys in object literals and class definitions, and in certain cases with duplicate keys might not report a missing pair for a getter/setter, like in the following example: ```js @@ -186,6 +245,8 @@ The code above creates an object with just a setter for the property `"a"`. See [no-dupe-keys](no-dupe-keys.md) if you also want to disallow duplicate keys in object literals. +See [no-dupe-class-members](no-dupe-class-members.md) if you also want to disallow duplicate names in class definitions. + ## When Not To Use It You can turn this rule off if you are not concerned with the simultaneous presence of setters and getters on objects. diff --git a/lib/rules/accessor-pairs.js b/lib/rules/accessor-pairs.js index 9c78bdc70e0..a33d1f32f2e 100644 --- a/lib/rules/accessor-pairs.js +++ b/lib/rules/accessor-pairs.js @@ -152,7 +152,7 @@ module.exports = { type: "suggestion", docs: { - description: "enforce getter and setter pairs in objects", + description: "enforce getter and setter pairs in objects and classes", category: "Best Practices", recommended: false, url: "https://eslint.org/docs/rules/accessor-pairs" @@ -168,6 +168,10 @@ module.exports = { setWithoutGet: { type: "boolean", default: true + }, + enforceForClassMembers: { + type: "boolean", + default: false } }, additionalProperties: false @@ -177,13 +181,16 @@ module.exports = { missingGetterInPropertyDescriptor: "Getter is not present in property descriptor.", missingSetterInPropertyDescriptor: "Setter is not present in property descriptor.", missingGetterInObjectLiteral: "Getter is not present for {{ name }}.", - missingSetterInObjectLiteral: "Setter is not present for {{ name }}." + missingSetterInObjectLiteral: "Setter is not present for {{ name }}.", + missingGetterInClass: "Getter is not present for class {{ name }}.", + missingSetterInClass: "Setter is not present for class {{ name }}." } }, create(context) { const config = context.options[0] || {}; const checkGetWithoutSet = config.getWithoutSet === true; const checkSetWithoutGet = config.setWithoutGet !== false; + const enforceForClassMembers = config.enforceForClassMembers === true; const sourceCode = context.getSourceCode(); /** @@ -201,6 +208,13 @@ module.exports = { loc: astUtils.getFunctionHeadLoc(node.value, sourceCode), data: { name: astUtils.getFunctionNameWithKind(node.value) } }); + } else if (node.type === "MethodDefinition") { + context.report({ + node, + messageId: `${messageKind}InClass`, + loc: astUtils.getFunctionHeadLoc(node.value, sourceCode), + data: { name: astUtils.getFunctionNameWithKind(node.value) } + }); } else { context.report({ node, @@ -313,15 +327,41 @@ module.exports = { } } - return { - ObjectExpression(node) { - if (checkSetWithoutGet || checkGetWithoutSet) { - checkObjectLiteral(node); - if (isPropertyDescriptor(node)) { - checkPropertyDescriptor(node); - } - } + /** + * Checks the given object expression as an object literal and as a possible property descriptor. + * @param {ASTNode} node `ObjectExpression` node to check. + * @returns {void} + * @private + */ + function checkObjectExpression(node) { + checkObjectLiteral(node); + if (isPropertyDescriptor(node)) { + checkPropertyDescriptor(node); } - }; + } + + /** + * Checks the given class body. + * @param {ASTNode} node `ClassBody` node to check. + * @returns {void} + * @private + */ + function checkClassBody(node) { + const methodDefinitions = node.body.filter(m => m.type === "MethodDefinition"); + + checkList(methodDefinitions.filter(m => m.static)); + checkList(methodDefinitions.filter(m => !m.static)); + } + + const listeners = {}; + + if (checkSetWithoutGet || checkGetWithoutSet) { + listeners.ObjectExpression = checkObjectExpression; + if (enforceForClassMembers) { + listeners.ClassBody = checkClassBody; + } + } + + return listeners; } }; diff --git a/tests/lib/rules/accessor-pairs.js b/tests/lib/rules/accessor-pairs.js index 4f55cd10eb7..c647a37f3a0 100644 --- a/tests/lib/rules/accessor-pairs.js +++ b/tests/lib/rules/accessor-pairs.js @@ -43,6 +43,10 @@ ruleTester.run("accessor-pairs", rule, { // Test default settings, this would be an error if `getWithoutSet` was set to `true` "var o = { get a() {} }", + { + code: "var o = { get a() {} }", + options: [{}] + }, // No accessors { @@ -319,7 +323,315 @@ ruleTester.run("accessor-pairs", rule, { "Object.create(null, {set: {value: function() {}}});", { code: "var o = {get: function() {}}", options: [{ getWithoutSet: true }] }, { code: "var o = {[set]: function() {}}", parserOptions: { ecmaVersion: 6 } }, - { code: "var set = 'value'; Object.defineProperty(obj, 'foo', {[set]: function(value) {}});", parserOptions: { ecmaVersion: 6 } } + { code: "var set = 'value'; Object.defineProperty(obj, 'foo', {[set]: function(value) {}});", parserOptions: { ecmaVersion: 6 } }, + + //------------------------------------------------------------------------------ + // Classes + //------------------------------------------------------------------------------ + + // Test default settings + { + code: "class A { set a(foo) {} }", + parserOptions: { ecmaVersion: 6 } + }, + { + code: "class A { get a() {} set b(foo) {} }", + options: [{}], + parserOptions: { ecmaVersion: 6 } + }, + { + code: "class A { get a() {} }", + options: [{ enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 } + }, + { + code: "class A { get a() {} }", + options: [{ setWithoutGet: true, getWithoutSet: true }], + parserOptions: { ecmaVersion: 6 } + }, + { + code: "class A { set a(foo) {} }", + options: [{ setWithoutGet: true, getWithoutSet: true }], + parserOptions: { ecmaVersion: 6 } + }, + { + code: "class A { static get a() {} }", + options: [{ setWithoutGet: true, getWithoutSet: true }], + parserOptions: { ecmaVersion: 6 } + }, + { + code: "class A { static set a(foo) {} }", + options: [{ setWithoutGet: true, getWithoutSet: true }], + parserOptions: { ecmaVersion: 6 } + }, + { + code: "A = class { get a() {} };", + options: [{ setWithoutGet: true, getWithoutSet: true }], + parserOptions: { ecmaVersion: 6 } + }, + { + code: "A = class { get a() {} set b(foo) {} };", + options: [{ setWithoutGet: true, getWithoutSet: true }], + parserOptions: { ecmaVersion: 6 } + }, + + // Explicitly disabled option + { + code: "class A { set a(foo) {} }", + options: [{ enforceForClassMembers: false }], + parserOptions: { ecmaVersion: 6 } + }, + { + code: "class A { get a() {} set b(foo) {} static get c() {} static set d(bar) {} }", + options: [{ setWithoutGet: true, getWithoutSet: true, enforceForClassMembers: false }], + parserOptions: { ecmaVersion: 6 } + }, + { + code: "(class A { get a() {} set b(foo) {} static get c() {} static set d(bar) {} });", + options: [{ setWithoutGet: true, getWithoutSet: true, enforceForClassMembers: false }], + parserOptions: { ecmaVersion: 6 } + }, + + // Disabled accessor kind options + { + code: "class A { get a() {} }", + options: [{ setWithoutGet: true, getWithoutSet: false, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 } + }, + { + code: "class A { set a(foo) {} }", + options: [{ setWithoutGet: false, getWithoutSet: true, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 } + }, + { + code: "class A { static get a() {} }", + options: [{ setWithoutGet: true, getWithoutSet: false, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 } + }, + { + code: "class A { static set a(foo) {} }", + options: [{ setWithoutGet: false, getWithoutSet: true, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 } + }, + { + code: "A = class { set a(foo) {} };", + options: [{ setWithoutGet: false, getWithoutSet: true, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 } + }, + { + code: "class A { get a() {} set b(foo) {} static get c() {} static set d(bar) {} }", + options: [{ setWithoutGet: false, getWithoutSet: false, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 } + }, + + // No accessors + { + code: "class A {}", + options: [{ setWithoutGet: true, getWithoutSet: true, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 } + }, + { + code: "(class {})", + options: [{ setWithoutGet: true, getWithoutSet: true, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 } + }, + { + code: "class A { constructor () {} }", + options: [{ setWithoutGet: true, getWithoutSet: true, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 } + }, + { + code: "class A { a() {} }", + options: [{ setWithoutGet: true, getWithoutSet: true, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 } + }, + { + code: "class A { static a() {} 'b'() {} }", + options: [{ setWithoutGet: true, getWithoutSet: true, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 } + }, + { + code: "class A { [a]() {} }", + options: [{ setWithoutGet: true, getWithoutSet: true, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 } + }, + { + code: "A = class { a() {} static a() {} b() {} static c() {} }", + options: [{ setWithoutGet: true, getWithoutSet: true, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 } + }, + + // Valid pairs with identifiers + { + code: "class A { get a() {} set a(foo) {} }", + options: [{ setWithoutGet: true, getWithoutSet: true, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 } + }, + { + code: "class A { set a(foo) {} get a() {} }", + options: [{ setWithoutGet: true, getWithoutSet: true, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 } + }, + { + code: "class A { static get a() {} static set a(foo) {} }", + options: [{ setWithoutGet: true, getWithoutSet: true, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 } + }, + { + code: "class A { static set a(foo) {} static get a() {} }", + options: [{ setWithoutGet: true, getWithoutSet: true, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 } + }, + { + code: "(class { set a(foo) {} get a() {} });", + options: [{ setWithoutGet: true, getWithoutSet: true, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 } + }, + + // Valid pairs with statically computed names + { + code: "class A { get 'a'() {} set ['a'](foo) {} }", + options: [{ setWithoutGet: true, getWithoutSet: true, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 } + }, + { + code: "class A { set [`a`](foo) {} get a() {} }", + options: [{ setWithoutGet: true, getWithoutSet: true, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 } + }, + { + code: "class A { get 'a'() {} set a(foo) {} }", + options: [{ setWithoutGet: true, getWithoutSet: true, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 } + }, + { + code: "A = class { static get 1e2() {} static set [100](foo) {} };", + options: [{ setWithoutGet: true, getWithoutSet: true, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 } + }, + + // Valid pairs with expressions + { + code: "class A { get [a]() {} set [a](foo) {} }", + options: [{ setWithoutGet: true, getWithoutSet: true, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 } + }, + { + code: "A = class { set [(f())](foo) {} get [(f())]() {} };", + options: [{ setWithoutGet: true, getWithoutSet: true, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 } + }, + { + code: "class A { static set [f(a)](foo) {} static get [f(a)]() {} }", + options: [{ setWithoutGet: true, getWithoutSet: true, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 } + }, + + // Multiple valid pairs in the same class + { + code: "class A { get a() {} set b(foo) {} set a(bar) {} get b() {} }", + options: [{ setWithoutGet: true, getWithoutSet: true, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 } + }, + { + code: "class A { get a() {} set a(bar) {} b() {} set c(foo) {} get c() {} }", + options: [{ setWithoutGet: true, getWithoutSet: true, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 } + }, + { + code: "(class { get a() {} static set a(foo) {} set a(bar) {} static get a() {} });", + options: [{ setWithoutGet: true, getWithoutSet: true, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 } + }, + + // Valid pairs with other elements + { + code: "class A { get a() {} b() {} set a(foo) {} }", + options: [{ setWithoutGet: true, getWithoutSet: true, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 } + }, + { + code: "class A { set a(foo) {} get a() {} b() {} }", + options: [{ setWithoutGet: true, getWithoutSet: true, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 } + }, + { + code: "class A { a() {} get b() {} c() {} set b(foo) {} d() {} }", + options: [{ setWithoutGet: true, getWithoutSet: true, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 } + }, + { + code: "class A { get a() {} set a(foo) {} static a() {} }", + options: [{ setWithoutGet: true, getWithoutSet: true, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 } + }, + { + code: "A = class { static get a() {} static b() {} static set a(foo) {} };", + options: [{ setWithoutGet: true, getWithoutSet: true, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 } + }, + { + code: "A = class { static set a(foo) {} static get a() {} a() {} };", + options: [{ setWithoutGet: true, getWithoutSet: true, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 } + }, + + // Duplicate keys. This is the responsibility of no-dupe-class-members, but this rule still checks if there is the other accessor kind. + { + code: "class A { get a() {} get a() {} set a(foo) {} }", + options: [{ setWithoutGet: true, getWithoutSet: true, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 } + }, + { + code: "class A { get [a]() {} set [a](foo) {} set [a](foo) {} }", + options: [{ setWithoutGet: true, getWithoutSet: true, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 } + }, + { + code: "class A { get a() {} set 'a'(foo) {} get [`a`]() {} }", + options: [{ setWithoutGet: true, getWithoutSet: true, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 } + }, + { + code: "A = class { get a() {} set a(foo) {} a() {} }", + options: [{ setWithoutGet: true, getWithoutSet: true, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 } + }, + { + code: "A = class { a() {} get a() {} set a(foo) {} }", + options: [{ setWithoutGet: true, getWithoutSet: true, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 } + }, + { + code: "class A { static set a(foo) {} static set a(foo) {} static get a() {} }", + options: [{ setWithoutGet: true, getWithoutSet: true, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 } + }, + { + code: "class A { static get a() {} static set a(foo) {} static get a() {} }", + options: [{ setWithoutGet: true, getWithoutSet: true, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 } + }, + { + code: "class A { static set a(foo) {} static get a() {} static a() {} }", + options: [{ setWithoutGet: true, getWithoutSet: true, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 } + }, + + /* + * This code should be invalid by this rule because it creates a class with the setter only, while the getter is ignored. + * However, this edge case is not covered, it should be reported by no-dupe-class-members anyway. + */ + { + code: "class A { get a() {} a() {} set a(foo) {} }", + options: [{ setWithoutGet: true, getWithoutSet: true, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 } + }, + { + code: "class A { static set a(foo) {} static a() {} static get a() {} }", + options: [{ setWithoutGet: true, getWithoutSet: true, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 } + } ], invalid: [ @@ -333,6 +645,11 @@ ruleTester.run("accessor-pairs", rule, { code: "var o = { set a(value) {} };", errors: [{ message: "Getter is not present for setter 'a'.", type: "Property" }] }, + { + code: "var o = { set a(value) {} };", + options: [{}], + errors: [{ message: "Getter is not present for setter 'a'.", type: "Property" }] + }, // Test that the options do not affect each other { @@ -530,7 +847,7 @@ ruleTester.run("accessor-pairs", rule, { code: "var o = { get ''() {}, set ' '(foo) {} };", options: [{ setWithoutGet: true, getWithoutSet: true }], errors: [ - { messageId: "missingSetterInObjectLiteral", type: "Property", column: 11 }, // TODO: Change to message when getFunctionNameWithKind gets fixed + { message: "Setter is not present for getter ''.", type: "Property", column: 11 }, { message: "Getter is not present for setter ' '.", type: "Property", column: 24 } ] }, @@ -538,7 +855,7 @@ ruleTester.run("accessor-pairs", rule, { code: "var o = { get ''() {}, set null(foo) {} };", options: [{ setWithoutGet: true, getWithoutSet: true }], errors: [ - { messageId: "missingSetterInObjectLiteral", type: "Property", column: 11 }, // TODO: Change to message when getFunctionNameWithKind gets fixed + { message: "Setter is not present for getter ''.", type: "Property", column: 11 }, { message: "Getter is not present for setter 'null'.", type: "Property", column: 24 } ] }, @@ -766,7 +1083,6 @@ ruleTester.run("accessor-pairs", rule, { { code: "var o = { get a() {} };", options: [{ setWithoutGet: true, getWithoutSet: true }], - parserOptions: { ecmaVersion: 2018 }, errors: [{ message: "Setter is not present for getter 'a'.", type: "Property", @@ -779,7 +1095,7 @@ ruleTester.run("accessor-pairs", rule, { { code: "var o = {\n set [\n a](foo) {} };", options: [{ setWithoutGet: true, getWithoutSet: true }], - parserOptions: { ecmaVersion: 2018 }, + parserOptions: { ecmaVersion: 2015 }, errors: [{ message: "Getter is not present for setter.", type: "Property", @@ -809,6 +1125,605 @@ ruleTester.run("accessor-pairs", rule, { { code: "Object.create(null, {foo: {set: function(value) {}}});", errors: [{ message: "Getter is not present in property descriptor.", type: "ObjectExpression" }] + }, + + //------------------------------------------------------------------------------ + // Classes + //------------------------------------------------------------------------------ + + // Test default settings + { + code: "class A { set a(value) {} }", + options: [{ enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 }, + errors: [{ message: "Getter is not present for class setter 'a'.", type: "MethodDefinition" }] + }, + { + code: "class A { static set a(value) {} }", + options: [{ enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 }, + errors: [{ message: "Getter is not present for class static setter 'a'.", type: "MethodDefinition" }] + }, + { + code: "A = class { set a(value) {} };", + options: [{ enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 }, + errors: [{ message: "Getter is not present for class setter 'a'.", type: "MethodDefinition" }] + }, + { + code: "(class A { static set a(value) {} });", + options: [{ enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 }, + errors: [{ message: "Getter is not present for class static setter 'a'.", type: "MethodDefinition" }] + }, + + // Test that the accessor kind options do not affect each other + { + code: "class A { set a(value) {} }", + options: [{ setWithoutGet: true, getWithoutSet: false, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 }, + errors: [{ message: "Getter is not present for class setter 'a'.", type: "MethodDefinition" }] + }, + { + code: "A = class { static set a(value) {} };", + options: [{ setWithoutGet: true, getWithoutSet: true, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 }, + errors: [{ message: "Getter is not present for class static setter 'a'.", type: "MethodDefinition" }] + }, + { + code: "let foo = class A { get a() {} };", + options: [{ setWithoutGet: false, getWithoutSet: true, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 }, + errors: [{ message: "Setter is not present for class getter 'a'.", type: "MethodDefinition" }] + }, + { + code: "class A { static get a() {} };", + options: [{ setWithoutGet: true, getWithoutSet: true, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 }, + errors: [{ message: "Setter is not present for class static getter 'a'.", type: "MethodDefinition" }] + }, + { + code: "(class { get a() {} });", + options: [{ getWithoutSet: true, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 }, + errors: [{ message: "Setter is not present for class getter 'a'.", type: "MethodDefinition" }] + }, + + // Various kinds of keys + { + code: "class A { get abc() {} }", + options: [{ setWithoutGet: true, getWithoutSet: true, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 }, + errors: [{ message: "Setter is not present for class getter 'abc'.", type: "MethodDefinition" }] + }, + { + code: "A = class { static set 'abc'(foo) {} };", + options: [{ setWithoutGet: true, getWithoutSet: true, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 }, + errors: [{ message: "Getter is not present for class static setter 'abc'.", type: "MethodDefinition" }] + }, + { + code: "(class { get 123() {} });", + options: [{ setWithoutGet: true, getWithoutSet: true, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 }, + errors: [{ message: "Setter is not present for class getter '123'.", type: "MethodDefinition" }] + }, + { + code: "class A { static get 1e2() {} }", + options: [{ setWithoutGet: true, getWithoutSet: true, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 }, + errors: [{ message: "Setter is not present for class static getter '100'.", type: "MethodDefinition" }] + }, + { + code: "A = class { get ['abc']() {} };", + options: [{ setWithoutGet: true, getWithoutSet: true, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 }, + errors: [{ message: "Setter is not present for class getter 'abc'.", type: "MethodDefinition" }] + }, + { + code: "class A { set [`abc`](foo) {} }", + options: [{ setWithoutGet: true, getWithoutSet: true, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 }, + errors: [{ message: "Getter is not present for class setter 'abc'.", type: "MethodDefinition" }] + }, + { + code: "class A { static get [123]() {} }", + options: [{ setWithoutGet: true, getWithoutSet: true, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 }, + errors: [{ message: "Setter is not present for class static getter '123'.", type: "MethodDefinition" }] + }, + { + code: "class A { get [abc]() {} }", + options: [{ setWithoutGet: true, getWithoutSet: true, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 }, + errors: [{ message: "Setter is not present for class getter.", type: "MethodDefinition" }] + }, + { + code: "class A { static get [f(abc)]() {} }", + options: [{ setWithoutGet: true, getWithoutSet: true, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 }, + errors: [{ message: "Setter is not present for class static getter.", type: "MethodDefinition" }] + }, + { + code: "A = class { set [a + b](foo) {} };", + options: [{ setWithoutGet: true, getWithoutSet: true, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 }, + errors: [{ message: "Getter is not present for class setter.", type: "MethodDefinition" }] + }, + { + code: "class A { get ['constructor']() {} }", + options: [{ setWithoutGet: true, getWithoutSet: true, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 }, + errors: [{ message: "Setter is not present for class getter 'constructor'.", type: "MethodDefinition" }] + }, + + // Different keys + { + code: "class A { get a() {} set b(foo) {} }", + options: [{ setWithoutGet: true, getWithoutSet: true, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 }, + errors: [ + { message: "Setter is not present for class getter 'a'.", type: "MethodDefinition", column: 11 }, + { message: "Getter is not present for class setter 'b'.", type: "MethodDefinition", column: 22 } + ] + }, + { + code: "A = class { set a(foo) {} get b() {} }", + options: [{ setWithoutGet: true, getWithoutSet: true, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 }, + errors: [ + { message: "Getter is not present for class setter 'a'.", type: "MethodDefinition", column: 13 }, + { message: "Setter is not present for class getter 'b'.", type: "MethodDefinition", column: 27 } + ] + }, + { + code: "A = class { static get a() {} static set b(foo) {} }", + options: [{ setWithoutGet: true, getWithoutSet: true, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 }, + errors: [ + { message: "Setter is not present for class static getter 'a'.", type: "MethodDefinition", column: 13 }, + { message: "Getter is not present for class static setter 'b'.", type: "MethodDefinition", column: 31 } + ] + }, + { + code: "class A { get a() {} set b(foo) {} }", + options: [{ setWithoutGet: false, getWithoutSet: true, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 }, + errors: [ + { message: "Setter is not present for class getter 'a'.", type: "MethodDefinition", column: 11 } + ] + }, + { + code: "class A { get a() {} set b(foo) {} }", + options: [{ setWithoutGet: true, getWithoutSet: false, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 }, + errors: [ + { message: "Getter is not present for class setter 'b'.", type: "MethodDefinition", column: 22 } + ] + }, + { + code: "class A { get 'a '() {} set 'a'(foo) {} }", + options: [{ setWithoutGet: true, getWithoutSet: true, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 }, + errors: [ + { message: "Setter is not present for class getter 'a '.", type: "MethodDefinition", column: 11 }, + { message: "Getter is not present for class setter 'a'.", type: "MethodDefinition", column: 25 } + ] + }, + { + code: "class A { get 'a'() {} set 1(foo) {} }", + options: [{ setWithoutGet: true, getWithoutSet: true, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 }, + errors: [ + { message: "Setter is not present for class getter 'a'.", type: "MethodDefinition", column: 11 }, + { message: "Getter is not present for class setter '1'.", type: "MethodDefinition", column: 24 } + ] + }, + { + code: "class A { get 1() {} set 2(foo) {} }", + options: [{ setWithoutGet: true, getWithoutSet: true, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 }, + errors: [ + { message: "Setter is not present for class getter '1'.", type: "MethodDefinition", column: 11 }, + { message: "Getter is not present for class setter '2'.", type: "MethodDefinition", column: 22 } + ] + }, + { + code: "class A { get ''() {} set null(foo) {} }", + options: [{ setWithoutGet: true, getWithoutSet: true, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 }, + errors: [ + { message: "Setter is not present for class getter ''.", type: "MethodDefinition", column: 11 }, + { message: "Getter is not present for class setter 'null'.", type: "MethodDefinition", column: 23 } + ] + }, + { + code: "class A { get a() {} set [a](foo) {} }", + options: [{ setWithoutGet: true, getWithoutSet: true, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 }, + errors: [ + { message: "Setter is not present for class getter 'a'.", type: "MethodDefinition", column: 11 }, + { message: "Getter is not present for class setter.", type: "MethodDefinition", column: 22 } + ] + }, + { + code: "class A { get [a]() {} set [b](foo) {} }", + options: [{ setWithoutGet: true, getWithoutSet: true, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 }, + errors: [ + { message: "Setter is not present for class getter.", type: "MethodDefinition", column: 11 }, + { message: "Getter is not present for class setter.", type: "MethodDefinition", column: 24 } + ] + }, + { + code: "class A { get [a]() {} set [a++](foo) {} }", + options: [{ setWithoutGet: true, getWithoutSet: true, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 }, + errors: [ + { message: "Setter is not present for class getter.", type: "MethodDefinition", column: 11 }, + { message: "Getter is not present for class setter.", type: "MethodDefinition", column: 24 } + ] + }, + { + code: "class A { get [a + b]() {} set [a - b](foo) {} }", + options: [{ setWithoutGet: true, getWithoutSet: true, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 }, + errors: [ + { message: "Setter is not present for class getter.", type: "MethodDefinition", column: 11 }, + { message: "Getter is not present for class setter.", type: "MethodDefinition", column: 28 } + ] + }, + + // Prototype and static accessors with same keys + { + code: "class A { get a() {} static set a(foo) {} }", + options: [{ setWithoutGet: true, getWithoutSet: true, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 }, + errors: [ + { message: "Setter is not present for class getter 'a'.", type: "MethodDefinition", column: 11 }, + { message: "Getter is not present for class static setter 'a'.", type: "MethodDefinition", column: 22 } + ] + }, + { + code: "A = class { static get a() {} set a(foo) {} };", + options: [{ setWithoutGet: true, getWithoutSet: true, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 }, + errors: [ + { message: "Setter is not present for class static getter 'a'.", type: "MethodDefinition", column: 13 }, + { message: "Getter is not present for class setter 'a'.", type: "MethodDefinition", column: 31 } + ] + }, + { + code: "class A { set [a](foo) {} static get [a]() {} }", + options: [{ setWithoutGet: true, getWithoutSet: true, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 }, + errors: [ + { message: "Getter is not present for class setter.", type: "MethodDefinition", column: 11 }, + { message: "Setter is not present for class static getter.", type: "MethodDefinition", column: 27 } + ] + }, + { + code: "class A { static set [a](foo) {} get [a]() {} }", + options: [{ setWithoutGet: true, getWithoutSet: true, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 }, + errors: [ + { message: "Getter is not present for class static setter.", type: "MethodDefinition", column: 11 }, + { message: "Setter is not present for class getter.", type: "MethodDefinition", column: 34 } + ] + }, + + // Multiple invalid of same and different kinds + { + code: "class A { get a() {} get b() {} }", + options: [{ setWithoutGet: true, getWithoutSet: true, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 }, + errors: [ + { message: "Setter is not present for class getter 'a'.", type: "MethodDefinition", column: 11 }, + { message: "Setter is not present for class getter 'b'.", type: "MethodDefinition", column: 22 } + ] + }, + { + code: "A = class { get a() {} get [b]() {} }", + options: [{ setWithoutGet: true, getWithoutSet: true, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 }, + errors: [ + { message: "Setter is not present for class getter 'a'.", type: "MethodDefinition", column: 13 }, + { message: "Setter is not present for class getter.", type: "MethodDefinition", column: 24 } + ] + }, + { + code: "class A { get [a]() {} get [b]() {} }", + options: [{ setWithoutGet: true, getWithoutSet: true, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 }, + errors: [ + { message: "Setter is not present for class getter.", type: "MethodDefinition", column: 11 }, + { message: "Setter is not present for class getter.", type: "MethodDefinition", column: 24 } + ] + }, + { + code: "A = class { set a(foo) {} set b(bar) {} };", + options: [{ setWithoutGet: true, getWithoutSet: true, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 }, + errors: [ + { message: "Getter is not present for class setter 'a'.", type: "MethodDefinition", column: 13 }, + { message: "Getter is not present for class setter 'b'.", type: "MethodDefinition", column: 27 } + ] + }, + { + code: "class A { static get a() {} static get b() {} }", + options: [{ setWithoutGet: true, getWithoutSet: true, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 }, + errors: [ + { message: "Setter is not present for class static getter 'a'.", type: "MethodDefinition", column: 11 }, + { message: "Setter is not present for class static getter 'b'.", type: "MethodDefinition", column: 29 } + ] + }, + { + code: "A = class { static set a(foo) {} static set b(bar) {} }", + options: [{ setWithoutGet: true, getWithoutSet: true, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 }, + errors: [ + { message: "Getter is not present for class static setter 'a'.", type: "MethodDefinition", column: 13 }, + { message: "Getter is not present for class static setter 'b'.", type: "MethodDefinition", column: 34 } + ] + }, + { + code: "class A { static get a() {} set b(foo) {} static set c(bar) {} get d() {} }", + options: [{ setWithoutGet: true, getWithoutSet: true, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 }, + errors: [ + { message: "Setter is not present for class static getter 'a'.", type: "MethodDefinition", column: 11 }, + { message: "Getter is not present for class setter 'b'.", type: "MethodDefinition", column: 29 }, + { message: "Getter is not present for class static setter 'c'.", type: "MethodDefinition", column: 43 }, + { message: "Setter is not present for class getter 'd'.", type: "MethodDefinition", column: 64 } + ] + }, + + // Checks per class + { + code: "class A { get a() {} } class B { set a(foo) {} }", + options: [{ setWithoutGet: true, getWithoutSet: true, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 }, + errors: [ + { message: "Setter is not present for class getter 'a'.", type: "MethodDefinition", column: 11 }, + { message: "Getter is not present for class setter 'a'.", type: "MethodDefinition", column: 34 } + ] + }, + { + code: "A = class { set a(foo) {} }, class { get a() {} };", + options: [{ setWithoutGet: true, getWithoutSet: true, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 }, + errors: [ + { message: "Getter is not present for class setter 'a'.", type: "MethodDefinition", column: 13 }, + { message: "Setter is not present for class getter 'a'.", type: "MethodDefinition", column: 38 } + ] + }, + { + code: "A = class { get a() {} }, { set a(foo) {} }", + options: [{ setWithoutGet: true, getWithoutSet: true, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 }, + errors: [ + { message: "Setter is not present for class getter 'a'.", type: "MethodDefinition", column: 13 }, + { message: "Getter is not present for setter 'a'.", type: "Property", column: 29 } + ] + }, + { + code: "A = { get a() {} }, class { set a(foo) {} }", + options: [{ setWithoutGet: true, getWithoutSet: true, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 }, + errors: [ + { message: "Setter is not present for getter 'a'.", type: "Property", column: 7 }, + { message: "Getter is not present for class setter 'a'.", type: "MethodDefinition", column: 29 } + ] + }, + + // Combinations or valid and invalid + { + code: "class A { get a() {} get b() {} set b(foo) {} }", + options: [{ setWithoutGet: true, getWithoutSet: true, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 }, + errors: [{ message: "Setter is not present for class getter 'a'.", type: "MethodDefinition", column: 11 }] + }, + { + code: "A = class { get b() {} get a() {} set b(foo) {} };", + options: [{ setWithoutGet: true, getWithoutSet: true, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 }, + errors: [{ message: "Setter is not present for class getter 'a'.", type: "MethodDefinition", column: 24 }] + }, + { + code: "class A { set b(foo) {} get b() {} set a(bar) {} }", + options: [{ setWithoutGet: true, getWithoutSet: true, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 }, + errors: [{ message: "Getter is not present for class setter 'a'.", type: "MethodDefinition", column: 36 }] + }, + { + code: "A = class { static get b() {} set a(foo) {} static set b(bar) {} };", + options: [{ setWithoutGet: true, getWithoutSet: true, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 }, + errors: [{ message: "Getter is not present for class setter 'a'.", type: "MethodDefinition", column: 31 }] + }, + { + code: "class A { static set a(foo) {} get b() {} set b(bar) {} }", + options: [{ setWithoutGet: true, getWithoutSet: true, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 }, + errors: [{ message: "Getter is not present for class static setter 'a'.", type: "MethodDefinition", column: 11 }] + }, + { + code: "class A { get b() {} static get a() {} set b(bar) {} }", + options: [{ setWithoutGet: true, getWithoutSet: true, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 }, + errors: [{ message: "Setter is not present for class static getter 'a'.", type: "MethodDefinition", column: 22 }] + }, + { + code: "class A { static set b(foo) {} static get a() {} static get b() {} }", + options: [{ setWithoutGet: true, getWithoutSet: true, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 }, + errors: [{ message: "Setter is not present for class static getter 'a'.", type: "MethodDefinition", column: 32 }] + }, + { + code: "class A { get [v1](){} static set i1(foo){} static set v2(bar){} get [i2](){} static get i3(){} set [v1](baz){} static get v2(){} set i4(quux){} }", + options: [{ setWithoutGet: true, getWithoutSet: true, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 }, + errors: [ + { message: "Getter is not present for class static setter 'i1'.", type: "MethodDefinition", column: 24 }, + { message: "Setter is not present for class getter.", type: "MethodDefinition", column: 66 }, + { message: "Setter is not present for class static getter 'i3'.", type: "MethodDefinition", column: 79 }, + { message: "Getter is not present for class setter 'i4'.", type: "MethodDefinition", column: 131 } + ] + }, + + // In the case of duplicates which don't have the other kind, all nodes are reported + { + code: "class A { get a() {} get a() {} }", + options: [{ setWithoutGet: true, getWithoutSet: true, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 }, + errors: [ + { message: "Setter is not present for class getter 'a'.", type: "MethodDefinition", column: 11 }, + { message: "Setter is not present for class getter 'a'.", type: "MethodDefinition", column: 22 } + ] + }, + { + code: "A = class { set a(foo) {} set a(foo) {} };", + options: [{ setWithoutGet: true, getWithoutSet: true, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 }, + errors: [ + { message: "Getter is not present for class setter 'a'.", type: "MethodDefinition", column: 13 }, + { message: "Getter is not present for class setter 'a'.", type: "MethodDefinition", column: 27 } + ] + }, + { + code: "A = class { static get a() {} static get a() {} };", + options: [{ setWithoutGet: true, getWithoutSet: true, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 }, + errors: [ + { message: "Setter is not present for class static getter 'a'.", type: "MethodDefinition", column: 13 }, + { message: "Setter is not present for class static getter 'a'.", type: "MethodDefinition", column: 31 } + ] + }, + { + code: "class A { set a(foo) {} set a(foo) {} }", + options: [{ setWithoutGet: true, getWithoutSet: true, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 }, + errors: [ + { message: "Getter is not present for class setter 'a'.", type: "MethodDefinition", column: 11 }, + { message: "Getter is not present for class setter 'a'.", type: "MethodDefinition", column: 25 } + ] + }, + + // Other elements or even method duplicates in the same class do not affect this rule + { + code: "class A { a() {} get b() {} c() {} }", + options: [{ setWithoutGet: true, getWithoutSet: true, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 }, + errors: [ + { message: "Setter is not present for class getter 'b'.", type: "MethodDefinition", column: 18 } + ] + }, + { + code: "A = class { a() {} get b() {} c() {} set d(foo) {} };", + options: [{ setWithoutGet: true, getWithoutSet: true, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 }, + errors: [ + { message: "Setter is not present for class getter 'b'.", type: "MethodDefinition", column: 20 }, + { message: "Getter is not present for class setter 'd'.", type: "MethodDefinition", column: 38 } + ] + }, + { + code: "class A { static a() {} get b() {} static c() {} }", + options: [{ setWithoutGet: true, getWithoutSet: true, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 }, + errors: [ + { message: "Setter is not present for class getter 'b'.", type: "MethodDefinition", column: 25 } + ] + }, + { + code: "class A { a() {} get a() {} }", + options: [{ setWithoutGet: true, getWithoutSet: true, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 }, + errors: [ + { message: "Setter is not present for class getter 'a'.", type: "MethodDefinition", column: 18 } + ] + }, + { + code: "A = class { static a() {} set a(foo) {} };", + options: [{ setWithoutGet: true, getWithoutSet: true, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 }, + errors: [ + { message: "Getter is not present for class setter 'a'.", type: "MethodDefinition", column: 27 } + ] + }, + { + code: "class A { a() {} static get b() {} c() {} }", + options: [{ setWithoutGet: true, getWithoutSet: true, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 }, + errors: [ + { message: "Setter is not present for class static getter 'b'.", type: "MethodDefinition", column: 18 } + ] + }, + { + code: "A = class { static a() {} static set b(foo) {} static c() {} d() {} };", + options: [{ setWithoutGet: true, getWithoutSet: true, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 }, + errors: [ + { message: "Getter is not present for class static setter 'b'.", type: "MethodDefinition", column: 27 } + ] + }, + { + code: "class A { a() {} static get a() {} a() {} }", + options: [{ setWithoutGet: true, getWithoutSet: true, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 }, + errors: [ + { message: "Setter is not present for class static getter 'a'.", type: "MethodDefinition", column: 18 } + ] + }, + { + code: "class A { static set a(foo) {} static a() {} }", + options: [{ setWithoutGet: true, getWithoutSet: true, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 }, + errors: [ + { message: "Getter is not present for class static setter 'a'.", type: "MethodDefinition", column: 11 } + ] + }, + + // Full location tests + { + code: "class A { get a() {} };", + options: [{ setWithoutGet: true, getWithoutSet: true, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 }, + errors: [{ + message: "Setter is not present for class getter 'a'.", + type: "MethodDefinition", + line: 1, + column: 11, + endLine: 1, + endColumn: 16 + }] + }, + { + code: "A = class {\n set [\n a](foo) {} };", + options: [{ setWithoutGet: true, getWithoutSet: true, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 }, + errors: [{ + message: "Getter is not present for class setter.", + type: "MethodDefinition", + line: 2, + column: 3, + endLine: 3, + endColumn: 4 + }] + }, + { + code: "class A { static get a() {} };", + options: [{ setWithoutGet: true, getWithoutSet: true, enforceForClassMembers: true }], + parserOptions: { ecmaVersion: 6 }, + errors: [{ + message: "Setter is not present for class static getter 'a'.", + type: "MethodDefinition", + line: 1, + column: 11, + endLine: 1, + endColumn: 23 + }] } ] });