Skip to content

Commit

Permalink
Update: class-methods-use-this: exceptions option (fixes #7085) (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
ljharb authored and gyandeeps committed Sep 13, 2016
1 parent afd132a commit 22edd8a
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 3 deletions.
32 changes: 32 additions & 0 deletions docs/rules/class-methods-use-this.md
Expand Up @@ -83,3 +83,35 @@ class A {
}
}
```

## Options

### Exceptions

```
"class-methods-use-this": [<enabled>, { "exceptions": [<...exceptions>] }]
```

The `exceptions` option allows you to pass an array of method names for which you would like to ignore warnings.

Examples of **incorrect** code for this rule when used without exceptions:

```js
/*eslint class-methods-use-this: "error"*/

class A {
foo() {
}
}
```

Examples of **correct** code for this rule when used with exceptions:

```js
/*eslint class-methods-use-this: ["error", { "exceptions": ["foo"] }] */

class A {
foo() {
}
}
```
28 changes: 26 additions & 2 deletions lib/rules/class-methods-use-this.js
Expand Up @@ -16,9 +16,23 @@ module.exports = {
category: "Best Practices",
recommended: false
},
schema: []
schema: [{
type: "object",
properties: {
exceptMethods: {
type: "array",
items: {
type: "string"
}
}
},
additionalProperties: false
}]
},
create(context) {
const config = context.options[0] ? Object.assign({}, context.options[0]) : {};
const exceptMethods = new Set(config.exceptMethods || []);

const stack = [];

/**
Expand All @@ -41,6 +55,16 @@ module.exports = {
return !node.static && node.kind !== "constructor" && node.type === "MethodDefinition";
}

/**
* Check if the node is an instance method not excluded by config
* @param {ASTNode} node - node to check
* @returns {boolean} True if it is an instance method, and not excluded by config
* @private
*/
function isIncludedInstanceMethod(node) {
return isInstanceMethod(node) && !exceptMethods.has(node.key.name);
}

/**
* Checks if we are leaving a function that is a method, and reports if 'this' has not been used.
* Static methods and the constructor are exempt.
Expand All @@ -52,7 +76,7 @@ module.exports = {
function exitFunction(node) {
const methodUsesThis = stack.pop();

if (isInstanceMethod(node.parent) && !methodUsesThis) {
if (isIncludedInstanceMethod(node.parent) && !methodUsesThis) {
context.report({
node,
message: "Expected 'this' to be used by class method '{{classMethod}}'.",
Expand Down
19 changes: 18 additions & 1 deletion tests/lib/rules/class-methods-use-this.js
Expand Up @@ -29,7 +29,8 @@ ruleTester.run("class-methods-use-this", rule, {
{code: "class A { static foo() {} }", parserOptions: { ecmaVersion: 6 }},
{code: "({ a(){} });", parserOptions: { ecmaVersion: 6 }},
{code: "class A { foo() { () => this; } }", parserOptions: { ecmaVersion: 6 }},
{code: "({ a: function () {} });", parserOptions: { ecmaVersion: 6 }}
{code: "({ a: function () {} });", parserOptions: { ecmaVersion: 6 }},
{code: "class A { foo() {this} bar() {} }", parserOptions: { ecmaVersion: 6 }, options: [{ exceptMethods: ["bar"] }]}
],
invalid: [
{
Expand Down Expand Up @@ -80,6 +81,22 @@ ruleTester.run("class-methods-use-this", rule, {
errors: [
{type: "FunctionExpression", line: 1, column: 14, message: "Expected 'this' to be used by class method 'foo'."}
]
},
{
code: "class A { foo() {} bar() {} }",
parserOptions: { ecmaVersion: 6 },
options: [{ exceptMethods: ["bar"] }],
errors: [
{type: "FunctionExpression", line: 1, column: 14, message: "Expected 'this' to be used by class method 'foo'."},
]
},
{
code: "class A { foo() {} hasOwnProperty() {} }",
parserOptions: { ecmaVersion: 6 },
options: [{ exceptMethods: ["foo"] }],
errors: [
{type: "FunctionExpression", line: 1, column: 34, message: "Expected 'this' to be used by class method 'hasOwnProperty'."}
]
}
]
});

0 comments on commit 22edd8a

Please sign in to comment.