Skip to content

Commit

Permalink
New: add "no-unused-vars" (#41)
Browse files Browse the repository at this point in the history
* New: add "no-unused-vars"

* update tests

* support simple variables declarations

* support more patterns

* add more cases
  • Loading branch information
g-plane authored and aladdin-add committed Jun 8, 2019
1 parent 1761307 commit 332822c
Show file tree
Hide file tree
Showing 3 changed files with 307 additions and 0 deletions.
116 changes: 116 additions & 0 deletions lib/rules/no-unused-vars.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
/**
* @fileoverview Add fixer to rule no-unused-vars.
* @author Pig Fang <[email protected]>
*/
"use strict";

const ruleComposer = require("eslint-rule-composer");
const utils = require("../utils");

const rule = utils.getFixableRule("no-unused-vars", false);

/**
* Check if an expression has side effect.
*
* @param {Node} node AST node
* @returns {boolean} result
*/
function hasSideEffect(node) {
if (["Literal", "Identifier", "ThisExpression"].includes(node.type)) {
return false;
}

if (node.type === "MemberExpression") {
return hasSideEffect(node.object) || hasSideEffect(node.property);
}

if (node.type === "TemplateLiteral") {
return node.expressions.length !== 0;
}

if (node.type === "AssignmentExpression") {
return hasSideEffect(node.right);
}

return true;
}

module.exports = ruleComposer.mapReports(
rule,
(problem, { sourceCode }) => {
problem.fix = fixer => {
const { node } = problem;
const { parent } = node;

if (!parent) {
return null;
}
const grand = parent.parent;

switch (parent.type) {
case "ImportSpecifier":
case "ImportDefaultSpecifier":
case "ImportNamespaceSpecifier":
if (!grand) {
return null;
}

if (grand.specifiers.length === 1) {
return fixer.remove(grand);
}

if (parent !== grand.specifiers[grand.specifiers.length - 1]) {
const comma = sourceCode.getTokenAfter(parent, { filter: token => token.value === "," });

return [fixer.remove(parent), fixer.remove(comma)];
}

if (grand.specifiers.filter(specifier => specifier.type === "ImportSpecifier").length === 1) {
const start = sourceCode.getTokenBefore(parent, { filter: token => token.value === "," }),
end = sourceCode.getTokenAfter(parent, { filter: token => token.value === "}" });

return fixer.removeRange([start.range[0], end.range[1]]);
}

return fixer.removeRange([
sourceCode.getTokenBefore(parent, { filter: token => token.value === "," }).range[0],
parent.range[1]
]);
case "VariableDeclarator":
if (!grand) {
return null;
}

if (parent.init && hasSideEffect(parent.init)) {
return null;
}

if (grand.declarations.length === 1) {
return fixer.remove(grand);
}

if (parent !== grand.declarations[grand.declarations.length - 1]) {
const comma = sourceCode.getTokenAfter(parent, { filter: token => token.value === "," });

return [fixer.remove(parent), fixer.remove(comma)];
}

return [
fixer.remove(sourceCode.getTokenBefore(parent, { filter: token => token.value === "," })),
fixer.remove(parent)
];
case "AssignmentPattern":
if (hasSideEffect(parent.right)) {
return null;
}
return fixer.remove(parent);
case "RestElement":
case "Property":
return fixer.remove(parent);
default:
return null;
}
};
return problem;
}
);
1 change: 1 addition & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ Name | ✔️ | 🛠 | Description
[no-unneeded-ternary](https://eslint.org/docs/rules/no-unneeded-ternary) | | 🛠 | disallow ternary operators when simpler alternatives exist
[no-unsafe-negation](https://eslint.org/docs/rules/no-unsafe-negation) | | 🛠 | disallow negating the left operand of relational operators
[no-unused-labels](https://eslint.org/docs/rules/no-unused-labels) | | 🛠 | disallow unused labels
[no-unused-vars](https://eslint.org/docs/rules/no-unused-vars) | | 🛠 | disallow unused variables
[no-useless-computed-key](https://eslint.org/docs/rules/no-useless-computed-key) | | 🛠 | disallow unnecessary computed property keys in object literals
[no-useless-concat](https://eslint.org/docs/rules/no-useless-concat) | | 🛠 | disallow unnecessary concatenation of literals or template literals
[no-useless-rename](https://eslint.org/docs/rules/no-useless-rename) | | 🛠 | disallow renaming import, export, and destructured assignments to the same name
Expand Down
190 changes: 190 additions & 0 deletions tests/lib/rules/no-unused-vars.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
/**
* @fileoverview Tests for rule no-unused-vars.
* @author Pig Fang <[email protected]>
*/
"use strict";

//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------

const rule = require("../../../lib/rules/no-unused-vars");
const RuleTester = require("eslint").RuleTester;

//------------------------------------------------------------------------------
// Tests
//------------------------------------------------------------------------------

const ruleTester = new RuleTester();

ruleTester.run("no-unused-vars", rule, {
valid: [
{
code: "import 'm'",
parserOptions: { sourceType: "module", ecmaVersion: 6 }
}
],
invalid: [
{
code: "import * as m from 'm'",
parserOptions: { sourceType: "module", ecmaVersion: 6 },
output: "",
errors: [{ type: "Identifier" }]
},
{
code: "import m from 'm'",
parserOptions: { sourceType: "module", ecmaVersion: 6 },
output: "",
errors: [{ type: "Identifier" }]
},
{
code: "import {m} from 'm'",
parserOptions: { sourceType: "module", ecmaVersion: 6 },
output: "",
errors: [{ type: "Identifier" }]
},
{
code: "import {m1 as m2} from 'm'",
parserOptions: { sourceType: "module", ecmaVersion: 6 },
output: "",
errors: [{ type: "Identifier" }]
},
{
code: "import m, {b} from 'm'; b;",
parserOptions: { sourceType: "module", ecmaVersion: 6 },
output: "import {b} from 'm'; b;",
errors: [{ type: "Identifier" }]
},
{
code: "import {a, b} from 'm'; b;",
parserOptions: { sourceType: "module", ecmaVersion: 6 },
output: "import { b} from 'm'; b;",
errors: [{ type: "Identifier" }]
},
{
code: "import {a1 as a2, b} from 'm'; b;",
parserOptions: { sourceType: "module", ecmaVersion: 6 },
output: "import { b} from 'm'; b;",
errors: [{ type: "Identifier" }]
},
{
code: "import {a, b} from 'm'; a;",
parserOptions: { sourceType: "module", ecmaVersion: 6 },
output: "import {a} from 'm'; a;",
errors: [{ type: "Identifier" }]
},
{
code: "import m, {a} from 'm'; m;",
parserOptions: { sourceType: "module", ecmaVersion: 6 },
output: "import m from 'm'; m;",
errors: [{ type: "Identifier" }]
},
{
code: "var a",
output: "",
errors: [{ type: "Identifier" }]
},
{
code: "var a = b",
output: "",
errors: [{ type: "Identifier" }]
},
{
code: "var a = undefined",
output: "",
errors: [{ type: "Identifier" }]
},
{
code: "var a = null",
output: "",
errors: [{ type: "Identifier" }]
},
{
code: "var a = 'b'",
output: "",
errors: [{ type: "Identifier" }]
},
{
code: "var a = this",
output: "",
errors: [{ type: "Identifier" }]
},
{
code: "var a = `template`",
parserOptions: { ecmaVersion: 6 },
output: "",
errors: [{ type: "Identifier" }]
},
{
code: "var a = this.value",
output: "",
errors: [{ type: "Identifier" }]
},
{
code: "var a = b = c",
output: "",
errors: [{ type: "Identifier" }]
},
{
code: "var a = `template-${value}`",
parserOptions: { ecmaVersion: 6 },
output: null,
errors: [{ type: "Identifier" }]
},
{
code: "var a = b()",
output: null,
errors: [{ type: "Identifier" }]
},
{
code: "var a = (b()).c",
output: null,
errors: [{ type: "Identifier" }]
},
{
code: "var a = b = c()",
output: null,
errors: [{ type: "Identifier" }]
},
{
code: "var a = b, c = d; c;",
output: "var c = d; c;",
errors: [{ type: "Identifier" }]
},
{
code: "var a = b, c = d; a;",
output: "var a = b ; a;",
errors: [{ type: "Identifier" }]
},
{
code: "let {...a} = b",
output: "let {} = b",
parserOptions: { ecmaVersion: 2018 },
errors: [{ type: "Identifier" }]
},
{
code: "let {a} = b",
output: "let {} = b",
parserOptions: { ecmaVersion: 6 },
errors: [{ type: "Identifier" }]
},
{
code: "let {a1: a2} = b",
output: "let {} = b",
parserOptions: { ecmaVersion: 6 },
errors: [{ type: "Identifier" }]
},
{
code: "let {a = b} = c",
output: "let {} = c",
parserOptions: { ecmaVersion: 6 },
errors: [{ type: "Identifier" }]
},
{
code: "let {a = b()} = c",
output: null,
parserOptions: { ecmaVersion: 6 },
errors: [{ type: "Identifier" }]
}
]
});

0 comments on commit 332822c

Please sign in to comment.