Skip to content

Commit

Permalink
Update: Add never option for new-parens (refs #10034) (#11379)
Browse files Browse the repository at this point in the history
* Update: Add never option for new-parens (fixes #10034)

* Docs: Fix eslint comment in new-parens never example

* Chore: Add tests for new-parens explicit always and never with arguments

* Chore: Adjust wording and naming of new-parens unnecessary message
  • Loading branch information
pfgithub authored and kaicataldo committed May 25, 2019
1 parent b5fa149 commit cf9cce8
Show file tree
Hide file tree
Showing 3 changed files with 172 additions and 15 deletions.
38 changes: 34 additions & 4 deletions docs/rules/new-parens.md
@@ -1,4 +1,4 @@
# require parentheses when invoking a constructor with no arguments (new-parens)
# Require parentheses when invoking a constructor with no arguments (new-parens)

JavaScript allows the omission of parentheses when invoking a function via the `new` keyword and the constructor has no arguments. However, some coders believe that omitting the parentheses is inconsistent with the rest of the language and thus makes code less clear.

Expand All @@ -8,9 +8,18 @@ var person = new Person;

## Rule Details

This rule requires parentheses when invoking a constructor with no arguments using the `new` keyword in order to increase code clarity.
This rule can enforce or disallow parentheses when invoking a constructor with no arguments using the `new` keyword.

Examples of **incorrect** code for this rule:
## Options

This rule takes one option.

- `"always"` enforces parenthesis after a new constructor with no arguments (default)
- `"never"` enforces no parenthesis after a new constructor with no arguments

### always

Examples of **incorrect** code for this rule with the `"always"` option:

```js
/*eslint new-parens: "error"*/
Expand All @@ -19,11 +28,32 @@ var person = new Person;
var person = new (Person);
```

Examples of **correct** code for this rule:
Examples of **correct** code for this rule with the `"always"` option:

```js
/*eslint new-parens: "error"*/

var person = new Person();
var person = new (Person)();
```

### never

Examples of **incorrect** code for this rule with the `"never"` option:

```js
/*eslint new-parens: ["error", "never"]*/

var person = new Person();
var person = new (Person)();
```

Examples of **correct** code for this rule with the `"never"` option:

```js
/*eslint new-parens: ["error", "never"]*/

var person = new Person;
var person = (new Person);
var person = new Person("Name");
```
52 changes: 42 additions & 10 deletions lib/rules/new-parens.js
Expand Up @@ -24,38 +24,70 @@ module.exports = {
type: "layout",

docs: {
description: "require parentheses when invoking a constructor with no arguments",
description: "enforce or disallow parentheses when invoking a constructor with no arguments",
category: "Stylistic Issues",
recommended: false,
url: "https://eslint.org/docs/rules/new-parens"
},

fixable: "code",
schema: [],
schema: {
anyOf: [
{
type: "array",
items: [
{
enum: ["always", "never"]
}
],
minItems: 0,
maxItems: 1
}
]
},
messages: {
missing: "Missing '()' invoking a constructor."
missing: "Missing '()' invoking a constructor.",
unnecessary: "Unnecessary '()' invoking a constructor with no arguments."
}
},

create(context) {
const options = context.options;
const always = options[0] !== "never"; // Default is always

const sourceCode = context.getSourceCode();

return {
NewExpression(node) {
if (node.arguments.length !== 0) {
return; // shortcut: if there are arguments, there have to be parens
return; // if there are arguments, there have to be parens
}

const lastToken = sourceCode.getLastToken(node);
const hasLastParen = lastToken && astUtils.isClosingParenToken(lastToken);
const hasParens = hasLastParen && astUtils.isOpeningParenToken(sourceCode.getTokenBefore(lastToken));

if (!hasParens) {
context.report({
node,
messageId: "missing",
fix: fixer => fixer.insertTextAfter(node, "()")
});
if (always) {
if (!hasParens) {
context.report({
node,
messageId: "missing",
fix: fixer => fixer.insertTextAfter(node, "()")
});
}
} else {
if (hasParens) {
context.report({
node,
messageId: "unnecessary",
fix: fixer => [
fixer.remove(sourceCode.getTokenBefore(lastToken)),
fixer.remove(lastToken),
fixer.insertTextBefore(node, "("),
fixer.insertTextAfter(node, ")")
]
});
}
}
}
};
Expand Down
97 changes: 96 additions & 1 deletion tests/lib/rules/new-parens.js
Expand Up @@ -17,21 +17,46 @@ const parser = require("../../fixtures/fixture-parser"),
// Tests
//------------------------------------------------------------------------------
const error = { messageId: "missing", type: "NewExpression" };
const neverError = { messageId: "unnecessary", type: "NewExpression" };

const ruleTester = new RuleTester();

ruleTester.run("new-parens", rule, {
valid: [

// Default (Always)
"var a = new Date();",
"var a = new Date(function() {});",
"var a = new (Date)();",
"var a = new ((Date))();",
"var a = (new Date());",
"var a = new foo.Bar();",
"var a = (new Foo()).bar;",
{ code: "new Storage<RootState>('state');", parser: parser("typescript-parsers/new-parens") }
{
code: "new Storage<RootState>('state');",
parser: parser("typescript-parsers/new-parens")
},

// Explicit Always
{ code: "var a = new Date();", options: ["always"] },
{ code: "var a = new foo.Bar();", options: ["always"] },
{ code: "var a = (new Foo()).bar;", options: ["always"] },

// Never
{ code: "var a = new Date;", options: ["never"] },
{ code: "var a = new Date(function() {});", options: ["never"] },
{ code: "var a = new (Date);", options: ["never"] },
{ code: "var a = new ((Date));", options: ["never"] },
{ code: "var a = (new Date);", options: ["never"] },
{ code: "var a = new foo.Bar;", options: ["never"] },
{ code: "var a = (new Foo).bar;", options: ["never"] },
{ code: "var a = new Person('Name')", options: ["never"] },
{ code: "var a = new Person('Name', 12)", options: ["never"] },
{ code: "var a = new ((Person))('Name');", options: ["never"] }
],
invalid: [

// Default (Always)
{
code: "var a = new Date;",
output: "var a = new Date();",
Expand Down Expand Up @@ -73,6 +98,76 @@ ruleTester.run("new-parens", rule, {
code: "var a = (new Foo).bar;",
output: "var a = (new Foo()).bar;",
errors: [error]
},

// Explicit always
{
code: "var a = new Date;",
output: "var a = new Date();",
options: ["always"],
errors: [error]
},
{
code: "var a = new foo.Bar;",
output: "var a = new foo.Bar();",
options: ["always"],
errors: [error]
},
{
code: "var a = (new Foo).bar;",
output: "var a = (new Foo()).bar;",
options: ["always"],
errors: [error]
},

// Never
{
code: "var a = new Date();",
output: "var a = (new Date);",
options: ["never"],
errors: [neverError]
},
{
code: "var a = new Date()",
output: "var a = (new Date)",
options: ["never"],
errors: [neverError]
},
{
code: "var a = new (Date)();",
output: "var a = (new (Date));",
options: ["never"],
errors: [neverError]
},
{
code: "var a = new (Date)()",
output: "var a = (new (Date))",
options: ["never"],
errors: [neverError]
},
{
code: "var a = (new Date())",
output: "var a = ((new Date))",
options: ["never"],
errors: [neverError]
},
{
code: "var a = (new Date())()",
output: "var a = ((new Date))()",
options: ["never"],
errors: [neverError]
},
{
code: "var a = new foo.Bar();",
output: "var a = (new foo.Bar);",
options: ["never"],
errors: [neverError]
},
{
code: "var a = (new Foo()).bar;",
output: "var a = ((new Foo)).bar;",
options: ["never"],
errors: [neverError]
}
]
});

0 comments on commit cf9cce8

Please sign in to comment.