Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update: make no-implicit-coercion support autofixing. (fixes #7056) #7061

Merged
merged 3 commits into from Sep 8, 2016
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
70 changes: 40 additions & 30 deletions lib/rules/no-implicit-coercion.js
Expand Up @@ -169,6 +169,7 @@ module.exports = {
recommended: false
},

fixable: "code",
schema: [{
type: "object",
properties: {
Expand Down Expand Up @@ -197,38 +198,51 @@ module.exports = {
const options = parseOptions(context.options[0]);
const sourceCode = context.getSourceCode();

/**
* Reports an error and autofixes the node
* @param {ASTNode} node - An ast node to report the error on.
* @param {string} recommendation - The recommended code for the issue
* @returns {void}
*/
function report(node, recommendation) {
context.report({
node,
message: "use `{{recommendation}}` instead.",
data: {
recommendation
},
fix(fixer) {
return fixer.replaceText(node, recommendation);
}
});
}

return {
UnaryExpression(node) {
let operatorAllowed;

// !!foo
operatorAllowed = options.allow.indexOf("!!") >= 0;
if (!operatorAllowed && options.boolean && isDoubleLogicalNegating(node)) {
context.report(
node,
"use `Boolean({{code}})` instead.", {
code: sourceCode.getText(node.argument.argument)
});
const recommendation = `Boolean(${sourceCode.getText(node.argument.argument)})`;

report(node, recommendation);
}

// ~foo.indexOf(bar)
operatorAllowed = options.allow.indexOf("~") >= 0;
if (!operatorAllowed && options.boolean && isBinaryNegatingOfIndexOf(node)) {
context.report(
node,
"use `{{code}} !== -1` instead.", {
code: sourceCode.getText(node.argument)
});
const recommendation = `${sourceCode.getText(node.argument)} !== -1`;

report(node, recommendation);
}

// +foo
operatorAllowed = options.allow.indexOf("+") >= 0;
if (!operatorAllowed && options.number && node.operator === "+" && !isNumeric(node.argument)) {
context.report(
node,
"use `Number({{code}})` instead.", {
code: sourceCode.getText(node.argument)
});
const recommendation = `Number(${sourceCode.getText(node.argument)})`;

report(node, recommendation);
}
},

Expand All @@ -241,21 +255,17 @@ module.exports = {
const nonNumericOperand = !operatorAllowed && options.number && isMultiplyByOne(node) && getNonNumericOperand(node);

if (nonNumericOperand) {
context.report(
node,
"use `Number({{code}})` instead.", {
code: sourceCode.getText(nonNumericOperand)
});
const recommendation = `Number(${sourceCode.getText(nonNumericOperand)})`;

report(node, recommendation);
}

// "" + foo
operatorAllowed = options.allow.indexOf("+") >= 0;
if (!operatorAllowed && options.string && isConcatWithEmptyString(node)) {
context.report(
node,
"use `String({{code}})` instead.", {
code: sourceCode.getText(getNonEmptyOperand(node))
});
const recommendation = `String(${sourceCode.getText(getNonEmptyOperand(node, ""))})`;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as line 278 - getNonEmptyOperand only takes one argument (just node).


report(node, recommendation);
}
},

Expand All @@ -265,11 +275,11 @@ module.exports = {
const operatorAllowed = options.allow.indexOf("+") >= 0;

if (!operatorAllowed && options.string && isAppendEmptyString(node)) {
context.report(
node,
"use `{{code}} = String({{code}})` instead.", {
code: sourceCode.getText(getNonEmptyOperand(node))
});
const code = sourceCode.getText(getNonEmptyOperand(node, ""));

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getNonEmptyOperand only takes one argument (just node). Can we get rid of the second argument? Also, mind getting rid of this extra newline while you're at it?

const recommendation = `${code} = String(${code})`;

report(node, recommendation);
}
}
};
Expand Down
108 changes: 90 additions & 18 deletions tests/lib/rules/no-implicit-coercion.js
Expand Up @@ -84,23 +84,95 @@ ruleTester.run("no-implicit-coercion", rule, {
{code: "+42"}
],
invalid: [
{code: "!!foo", errors: [{message: "use `Boolean(foo)` instead.", type: "UnaryExpression"}]},
{code: "!!(foo + bar)", errors: [{message: "use `Boolean(foo + bar)` instead.", type: "UnaryExpression"}]},
{code: "~foo.indexOf(1)", errors: [{message: "use `foo.indexOf(1) !== -1` instead.", type: "UnaryExpression"}]},
{code: "~foo.bar.indexOf(2)", errors: [{message: "use `foo.bar.indexOf(2) !== -1` instead.", type: "UnaryExpression"}]},
{code: "+foo", errors: [{message: "use `Number(foo)` instead.", type: "UnaryExpression"}]},
{code: "+foo.bar", errors: [{message: "use `Number(foo.bar)` instead.", type: "UnaryExpression"}]},
{code: "1*foo", errors: [{message: "use `Number(foo)` instead.", type: "BinaryExpression"}]},
{code: "foo*1", errors: [{message: "use `Number(foo)` instead.", type: "BinaryExpression"}]},
{code: "1*foo.bar", errors: [{message: "use `Number(foo.bar)` instead.", type: "BinaryExpression"}]},
{code: "\"\"+foo", errors: [{message: "use `String(foo)` instead.", type: "BinaryExpression"}]},
{code: "foo+\"\"", errors: [{message: "use `String(foo)` instead.", type: "BinaryExpression"}]},
{code: "\"\"+foo.bar", errors: [{message: "use `String(foo.bar)` instead.", type: "BinaryExpression"}]},
{code: "foo += \"\"", errors: [{message: "use `foo = String(foo)` instead.", type: "AssignmentExpression"}]},
{code: "var a = !!foo", options: [{boolean: true, allow: ["~"]}], errors: [{message: "use `Boolean(foo)` instead.", type: "UnaryExpression"}]},
{code: "var a = ~foo.indexOf(1)", options: [{boolean: true, allow: ["!!"]}], errors: [{message: "use `foo.indexOf(1) !== -1` instead.", type: "UnaryExpression"}]},
{code: "var a = 1 * foo", options: [{boolean: true, allow: ["+"]}], errors: [{message: "use `Number(foo)` instead.", type: "BinaryExpression"}]},
{code: "var a = +foo", options: [{boolean: true, allow: ["*"]}], errors: [{message: "use `Number(foo)` instead.", type: "UnaryExpression"}]},
{code: "var a = \"\" + foo", options: [{boolean: true, allow: ["*"]}], errors: [{message: "use `String(foo)` instead.", type: "BinaryExpression"}]}
{
code: "!!foo",
errors: [{message: "use `Boolean(foo)` instead.", type: "UnaryExpression"}],
output: "Boolean(foo)"
},
{
code: "!!(foo + bar)",
errors: [{message: "use `Boolean(foo + bar)` instead.", type: "UnaryExpression"}],
output: "Boolean(foo + bar)"
},
{
code: "~foo.indexOf(1)",
errors: [{message: "use `foo.indexOf(1) !== -1` instead.", type: "UnaryExpression"}],
output: "foo.indexOf(1) !== -1"
},
{
code: "~foo.bar.indexOf(2)",
errors: [{message: "use `foo.bar.indexOf(2) !== -1` instead.", type: "UnaryExpression"}],
output: "foo.bar.indexOf(2) !== -1"
},
{
code: "+foo",
errors: [{message: "use `Number(foo)` instead.", type: "UnaryExpression"}],
output: "Number(foo)"
},
{
code: "+foo.bar",
errors: [{message: "use `Number(foo.bar)` instead.", type: "UnaryExpression"}],
output: "Number(foo.bar)"
},
{
code: "1*foo",
errors: [{message: "use `Number(foo)` instead.", type: "BinaryExpression"}],
output: "Number(foo)"
},
{
code: "foo*1",
errors: [{message: "use `Number(foo)` instead.", type: "BinaryExpression"}],
output: "Number(foo)"
},
{
code: "1*foo.bar",
errors: [{message: "use `Number(foo.bar)` instead.", type: "BinaryExpression"}],
output: "Number(foo.bar)"
},
{
code: "\"\"+foo",
errors: [{message: "use `String(foo)` instead.", type: "BinaryExpression"}],
output: "String(foo)"
},
{
code: "foo+\"\"",
errors: [{message: "use `String(foo)` instead.", type: "BinaryExpression"}],
output: "String(foo)"
},
{
code: "\"\"+foo.bar",
errors: [{message: "use `String(foo.bar)` instead.", type: "BinaryExpression"}],
output: "String(foo.bar)"
},
{
code: "foo += \"\"",
errors: [{message: "use `foo = String(foo)` instead.", type: "AssignmentExpression"}],
output: "foo = String(foo)"
},
{
code: "var a = !!foo", options: [{boolean: true, allow: ["~"]}],
errors: [{message: "use `Boolean(foo)` instead.", type: "UnaryExpression"}],
output: "var a = Boolean(foo)"
},
{
code: "var a = ~foo.indexOf(1)", options: [{boolean: true, allow: ["!!"]}],
errors: [{message: "use `foo.indexOf(1) !== -1` instead.", type: "UnaryExpression"}],
output: "var a = foo.indexOf(1) !== -1"
},
{
code: "var a = 1 * foo", options: [{boolean: true, allow: ["+"]}],
errors: [{message: "use `Number(foo)` instead.", type: "BinaryExpression"}],
output: "var a = Number(foo)"
},
{
code: "var a = +foo", options: [{boolean: true, allow: ["*"]}],
errors: [{message: "use `Number(foo)` instead.", type: "UnaryExpression"}],
output: "var a = Number(foo)"
},
{
code: "var a = \"\" + foo", options: [{boolean: true, allow: ["*"]}],
errors: [{message: "use `String(foo)` instead.", type: "BinaryExpression"}],
output: "var a = String(foo)"
}
]
});