Skip to content

Commit

Permalink
Update: make no-implicit-coercion support autofixing. (fixes #7056) (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
TheSavior authored and kaicataldo committed Sep 8, 2016
1 parent 883316d commit 538d258
Show file tree
Hide file tree
Showing 2 changed files with 129 additions and 48 deletions.
69 changes: 39 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))})`;

report(node, recommendation);
}
},

Expand All @@ -265,11 +275,10 @@ 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));
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)"
}
]
});

0 comments on commit 538d258

Please sign in to comment.