Skip to content

Commit

Permalink
New: allowAfterThisConstructor for no-underscore-dangle (fixes #11488) (
Browse files Browse the repository at this point in the history
  • Loading branch information
sripberger authored and platinumazure committed Oct 29, 2019
1 parent 287ca56 commit e17fb90
Show file tree
Hide file tree
Showing 3 changed files with 40 additions and 6 deletions.
12 changes: 12 additions & 0 deletions docs/rules/no-underscore-dangle.md
Expand Up @@ -42,6 +42,7 @@ This rule has an object option:
* `"allow"` allows specified identifiers to have dangling underscores
* `"allowAfterThis": false` (default) disallows dangling underscores in members of the `this` object
* `"allowAfterSuper": false` (default) disallows dangling underscores in members of the `super` object
* `"allowAfterThisConstructor": false` (default) disallows dangling underscores in members of the `this.constructor` object
* `"enforceInMethodNames": false` (default) allows dangling underscores in method names

### allow
Expand Down Expand Up @@ -77,6 +78,17 @@ var a = super.foo_;
super._bar();
```

### allowAfterThisConstructor

Examples of **correct** code for this rule with the `{ "allowAfterThisConstructor": true }` option:

```js
/*eslint no-underscore-dangle: ["error", { "allowAfterThisConstructor": true }]*/

var a = this.constructor.foo_;
this.constructor._bar();
```

### enforceInMethodNames

Examples of **incorrect** code for this rule with the `{ "enforceInMethodNames": true }` option:
Expand Down
27 changes: 23 additions & 4 deletions lib/rules/no-underscore-dangle.js
Expand Up @@ -38,6 +38,10 @@ module.exports = {
type: "boolean",
default: false
},
allowAfterThisConstructor: {
type: "boolean",
default: false
},
enforceInMethodNames: {
type: "boolean",
default: false
Expand All @@ -54,6 +58,7 @@ module.exports = {
const ALLOWED_VARIABLES = options.allow ? options.allow : [];
const allowAfterThis = typeof options.allowAfterThis !== "undefined" ? options.allowAfterThis : false;
const allowAfterSuper = typeof options.allowAfterSuper !== "undefined" ? options.allowAfterSuper : false;
const allowAfterThisConstructor = typeof options.allowAfterThisConstructor !== "undefined" ? options.allowAfterThisConstructor : false;
const enforceInMethodNames = typeof options.enforceInMethodNames !== "undefined" ? options.enforceInMethodNames : false;

//-------------------------------------------------------------------------
Expand All @@ -72,7 +77,7 @@ module.exports = {

/**
* Check if identifier has a underscore at the end
* @param {ASTNode} identifier node to evaluate
* @param {string} identifier name of the node
* @returns {boolean} true if its is present
* @private
*/
Expand All @@ -84,7 +89,7 @@ module.exports = {

/**
* Check if identifier is a special case member expression
* @param {ASTNode} identifier node to evaluate
* @param {string} identifier name of the node
* @returns {boolean} true if its is a special case
* @private
*/
Expand All @@ -94,7 +99,7 @@ module.exports = {

/**
* Check if identifier is a special case variable expression
* @param {ASTNode} identifier node to evaluate
* @param {string} identifier name of the node
* @returns {boolean} true if its is a special case
* @private
*/
Expand All @@ -104,6 +109,18 @@ module.exports = {
return identifier === "_";
}

/**
* Check if a node is a member reference of this.constructor
* @param {ASTNode} node node to evaluate
* @returns {boolean} true if it is a reference on this.constructor
* @private
*/
function isThisConstructorReference(node) {
return node.object.type === "MemberExpression" &&
node.object.property.name === "constructor" &&
node.object.object.type === "ThisExpression";
}

/**
* Check if function has a underscore at the end
* @param {ASTNode} node node to evaluate
Expand Down Expand Up @@ -156,11 +173,13 @@ module.exports = {
function checkForTrailingUnderscoreInMemberExpression(node) {
const identifier = node.property.name,
isMemberOfThis = node.object.type === "ThisExpression",
isMemberOfSuper = node.object.type === "Super";
isMemberOfSuper = node.object.type === "Super",
isMemberOfThisConstructor = isThisConstructorReference(node);

if (typeof identifier !== "undefined" && hasTrailingUnderscore(identifier) &&
!(isMemberOfThis && allowAfterThis) &&
!(isMemberOfSuper && allowAfterSuper) &&
!(isMemberOfThisConstructor && allowAfterThisConstructor) &&
!isSpecialCaseIdentifierForMemberExpression(identifier) && !isAllowed(identifier)) {
context.report({
node,
Expand Down
7 changes: 5 additions & 2 deletions tests/lib/rules/no-underscore-dangle.js
Expand Up @@ -38,7 +38,8 @@ ruleTester.run("no-underscore-dangle", rule, {
{ code: "const o = { _onClick() { } }", parserOptions: { ecmaVersion: 6 } },
{ code: "const o = { onClick_() { } }", parserOptions: { ecmaVersion: 6 } },
{ code: "const o = { _foo: 'bar' }", parserOptions: { ecmaVersion: 6 } },
{ code: "const o = { foo_: 'bar' }", parserOptions: { ecmaVersion: 6 } }
{ code: "const o = { foo_: 'bar' }", parserOptions: { ecmaVersion: 6 } },
{ code: "this.constructor._bar", options: [{ allowAfterThisConstructor: true }] }
],
invalid: [
{ code: "var _foo = 1", errors: [{ message: "Unexpected dangling '_' in '_foo'.", type: "VariableDeclarator" }] },
Expand All @@ -53,6 +54,8 @@ ruleTester.run("no-underscore-dangle", rule, {
{ code: "class foo { _onClick() { } }", options: [{ enforceInMethodNames: true }], parserOptions: { ecmaVersion: 6 }, errors: [{ message: "Unexpected dangling '_' in '_onClick'.", type: "MethodDefinition" }] },
{ code: "class foo { onClick_() { } }", options: [{ enforceInMethodNames: true }], parserOptions: { ecmaVersion: 6 }, errors: [{ message: "Unexpected dangling '_' in 'onClick_'.", type: "MethodDefinition" }] },
{ code: "const o = { _onClick() { } }", options: [{ enforceInMethodNames: true }], parserOptions: { ecmaVersion: 6 }, errors: [{ message: "Unexpected dangling '_' in '_onClick'.", type: "Property" }] },
{ code: "const o = { onClick_() { } }", options: [{ enforceInMethodNames: true }], parserOptions: { ecmaVersion: 6 }, errors: [{ message: "Unexpected dangling '_' in 'onClick_'.", type: "Property" }] }
{ code: "const o = { onClick_() { } }", options: [{ enforceInMethodNames: true }], parserOptions: { ecmaVersion: 6 }, errors: [{ message: "Unexpected dangling '_' in 'onClick_'.", type: "Property" }] },
{ code: "this.constructor._bar", errors: [{ message: "Unexpected dangling '_' in '_bar'.", type: "MemberExpression" }] },
{ code: "foo.constructor._bar", options: [{ allowAfterThisConstructor: true }], errors: [{ message: "Unexpected dangling '_' in '_bar'.", type: "MemberExpression" }] }
]
});

0 comments on commit e17fb90

Please sign in to comment.