aboutsummaryrefslogtreecommitdiff
path: root/node_modules/tslint/lib/rules/noUnusedVariableRule.js
diff options
context:
space:
mode:
Diffstat (limited to 'node_modules/tslint/lib/rules/noUnusedVariableRule.js')
-rw-r--r--node_modules/tslint/lib/rules/noUnusedVariableRule.js350
1 files changed, 350 insertions, 0 deletions
diff --git a/node_modules/tslint/lib/rules/noUnusedVariableRule.js b/node_modules/tslint/lib/rules/noUnusedVariableRule.js
new file mode 100644
index 000000000..79fcb599d
--- /dev/null
+++ b/node_modules/tslint/lib/rules/noUnusedVariableRule.js
@@ -0,0 +1,350 @@
+"use strict";
+/**
+ * @license
+ * Copyright 2014 Palantir Technologies, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+Object.defineProperty(exports, "__esModule", { value: true });
+var tslib_1 = require("tslib");
+var utils = require("tsutils");
+var ts = require("typescript");
+var Lint = require("../index");
+var OPTION_CHECK_PARAMETERS = "check-parameters";
+var OPTION_IGNORE_PATTERN = "ignore-pattern";
+var Rule = (function (_super) {
+ tslib_1.__extends(Rule, _super);
+ function Rule() {
+ return _super !== null && _super.apply(this, arguments) || this;
+ }
+ /* tslint:enable:object-literal-sort-keys */
+ Rule.prototype.applyWithProgram = function (sourceFile, program) {
+ var _this = this;
+ var x = program.getCompilerOptions();
+ if (x.noUnusedLocals === true && x.noUnusedParameters === true) {
+ console.warn("WARNING: 'no-unused-variable' lint rule does not need to be set if " +
+ "the 'no-unused-locals' and 'no-unused-parameters' compiler options are enabled.");
+ }
+ return this.applyWithFunction(sourceFile, function (ctx) { return walk(ctx, program, parseOptions(_this.ruleArguments)); });
+ };
+ return Rule;
+}(Lint.Rules.TypedRule));
+/* tslint:disable:object-literal-sort-keys */
+Rule.metadata = {
+ ruleName: "no-unused-variable",
+ description: (_a = ["Disallows unused imports, variables, functions and\n private class members. Similar to tsc's --noUnusedParameters and --noUnusedLocals\n options, but does not interrupt code compilation."], _a.raw = ["Disallows unused imports, variables, functions and\n private class members. Similar to tsc's --noUnusedParameters and --noUnusedLocals\n options, but does not interrupt code compilation."], Lint.Utils.dedent(_a)),
+ hasFix: true,
+ optionsDescription: (_b = ["\n Three optional arguments may be optionally provided:\n\n * `\"check-parameters\"` disallows unused function and constructor parameters.\n * NOTE: this option is experimental and does not work with classes\n that use abstract method declarations, among other things.\n * `{\"ignore-pattern\": \"pattern\"}` where pattern is a case-sensitive regexp.\n Variable names that match the pattern will be ignored."], _b.raw = ["\n Three optional arguments may be optionally provided:\n\n * \\`\"check-parameters\"\\` disallows unused function and constructor parameters.\n * NOTE: this option is experimental and does not work with classes\n that use abstract method declarations, among other things.\n * \\`{\"ignore-pattern\": \"pattern\"}\\` where pattern is a case-sensitive regexp.\n Variable names that match the pattern will be ignored."], Lint.Utils.dedent(_b)),
+ options: {
+ type: "array",
+ items: {
+ oneOf: [
+ {
+ type: "string",
+ enum: ["check-parameters"],
+ },
+ {
+ type: "object",
+ properties: {
+ "ignore-pattern": { type: "string" },
+ },
+ additionalProperties: false,
+ },
+ ],
+ },
+ minLength: 0,
+ maxLength: 3,
+ },
+ optionExamples: [true, [true, { "ignore-pattern": "^_" }]],
+ type: "functionality",
+ typescriptOnly: true,
+ requiresTypeInfo: true,
+};
+exports.Rule = Rule;
+function parseOptions(options) {
+ var checkParameters = options.indexOf(OPTION_CHECK_PARAMETERS) !== -1;
+ var ignorePattern;
+ for (var _i = 0, options_1 = options; _i < options_1.length; _i++) {
+ var o = options_1[_i];
+ if (typeof o === "object") {
+ // tslint:disable-next-line no-unsafe-any
+ var ignore = o[OPTION_IGNORE_PATTERN];
+ if (ignore != null) {
+ ignorePattern = new RegExp(ignore);
+ break;
+ }
+ }
+ }
+ return { checkParameters: checkParameters, ignorePattern: ignorePattern };
+}
+function walk(ctx, program, _a) {
+ var checkParameters = _a.checkParameters, ignorePattern = _a.ignorePattern;
+ var sourceFile = ctx.sourceFile;
+ var unusedCheckedProgram = getUnusedCheckedProgram(program, checkParameters);
+ var diagnostics = ts.getPreEmitDiagnostics(unusedCheckedProgram, sourceFile);
+ var checker = unusedCheckedProgram.getTypeChecker(); // Doesn't matter which program is used for this.
+ // If all specifiers in an import are unused, we elide the entire import.
+ var importSpecifierFailures = new Map();
+ for (var _i = 0, diagnostics_1 = diagnostics; _i < diagnostics_1.length; _i++) {
+ var diag = diagnostics_1[_i];
+ var kind = getUnusedDiagnostic(diag);
+ if (kind === undefined) {
+ continue;
+ }
+ var failure = ts.flattenDiagnosticMessageText(diag.messageText, "\n");
+ if (kind === 0 /* VARIABLE_OR_PARAMETER */) {
+ var importName = findImport(diag.start, sourceFile);
+ if (importName !== undefined) {
+ if (isImportUsed(importName, sourceFile, checker)) {
+ continue;
+ }
+ if (importSpecifierFailures.has(importName)) {
+ throw new Error("Should not get 2 errors for the same import.");
+ }
+ importSpecifierFailures.set(importName, failure);
+ continue;
+ }
+ }
+ if (ignorePattern !== undefined) {
+ var varName = /'(.*)'/.exec(failure)[1];
+ if (ignorePattern.test(varName)) {
+ continue;
+ }
+ }
+ ctx.addFailureAt(diag.start, diag.length, failure);
+ }
+ if (importSpecifierFailures.size !== 0) {
+ addImportSpecifierFailures(ctx, importSpecifierFailures, sourceFile);
+ }
+}
+/**
+ * Handle import-specifier failures separately.
+ * - If all of the import specifiers in an import are unused, add a combined failure for them all.
+ * - Unused imports are fixable.
+ */
+function addImportSpecifierFailures(ctx, failures, sourceFile) {
+ // tslint:disable return-undefined
+ // (fixed in tslint 5.3)
+ forEachImport(sourceFile, function (importNode) {
+ if (importNode.kind === ts.SyntaxKind.ImportEqualsDeclaration) {
+ tryRemoveAll(importNode.name);
+ return;
+ }
+ if (importNode.importClause === undefined) {
+ // Error node
+ return;
+ }
+ var _a = importNode.importClause, defaultName = _a.name, namedBindings = _a.namedBindings;
+ if (namedBindings !== undefined && namedBindings.kind === ts.SyntaxKind.NamespaceImport) {
+ tryRemoveAll(namedBindings.name);
+ return;
+ }
+ var allNamedBindingsAreFailures = namedBindings === undefined || namedBindings.elements.every(function (e) { return failures.has(e.name); });
+ if (namedBindings !== undefined && allNamedBindingsAreFailures) {
+ for (var _i = 0, _b = namedBindings.elements; _i < _b.length; _i++) {
+ var e = _b[_i];
+ failures.delete(e.name);
+ }
+ }
+ if ((defaultName === undefined || failures.has(defaultName)) && allNamedBindingsAreFailures) {
+ if (defaultName !== undefined) {
+ failures.delete(defaultName);
+ }
+ removeAll(importNode, "All imports are unused.");
+ return;
+ }
+ if (defaultName !== undefined) {
+ var failure = tryDelete(defaultName);
+ if (failure !== undefined) {
+ var start = defaultName.getStart();
+ var end = namedBindings !== undefined ? namedBindings.getStart() : importNode.moduleSpecifier.getStart();
+ var fix = Lint.Replacement.deleteFromTo(start, end);
+ ctx.addFailureAtNode(defaultName, failure, fix);
+ }
+ }
+ if (namedBindings !== undefined) {
+ if (allNamedBindingsAreFailures) {
+ var start = defaultName !== undefined ? defaultName.getEnd() : namedBindings.getStart();
+ var fix = Lint.Replacement.deleteFromTo(start, namedBindings.getEnd());
+ var failure = "All named bindings are unused.";
+ ctx.addFailureAtNode(namedBindings, failure, fix);
+ }
+ else {
+ var elements = namedBindings.elements;
+ for (var i = 0; i < elements.length; i++) {
+ var element = elements[i];
+ var failure = tryDelete(element.name);
+ if (failure === undefined) {
+ continue;
+ }
+ var prevElement = elements[i - 1];
+ var nextElement = elements[i + 1];
+ var start = prevElement !== undefined ? prevElement.getEnd() : element.getStart();
+ var end = nextElement !== undefined && prevElement == undefined ? nextElement.getStart() : element.getEnd();
+ var fix = Lint.Replacement.deleteFromTo(start, end);
+ ctx.addFailureAtNode(element.name, failure, fix);
+ }
+ }
+ }
+ function tryRemoveAll(name) {
+ var failure = tryDelete(name);
+ if (failure !== undefined) {
+ removeAll(name, failure);
+ }
+ }
+ function removeAll(errorNode, failure) {
+ var fix = Lint.Replacement.deleteFromTo(importNode.getStart(), importNode.getEnd());
+ ctx.addFailureAtNode(errorNode, failure, fix);
+ }
+ });
+ if (failures.size !== 0) {
+ throw new Error("Should have revisited all import specifier failures.");
+ }
+ function tryDelete(name) {
+ var failure = failures.get(name);
+ if (failure !== undefined) {
+ failures.delete(name);
+ return failure;
+ }
+ return undefined;
+ }
+}
+/**
+ * Ignore this import if it's used as an implicit type somewhere.
+ * Workround for https://github.com/Microsoft/TypeScript/issues/9944
+ */
+function isImportUsed(importSpecifier, sourceFile, checker) {
+ var importedSymbol = checker.getSymbolAtLocation(importSpecifier);
+ if (importedSymbol === undefined) {
+ return false;
+ }
+ var symbol = checker.getAliasedSymbol(importedSymbol);
+ if (!Lint.isSymbolFlagSet(symbol, ts.SymbolFlags.Type)) {
+ return false;
+ }
+ return ts.forEachChild(sourceFile, function cb(child) {
+ if (isImportLike(child)) {
+ return false;
+ }
+ var type = getImplicitType(child, checker);
+ // TODO: checker.typeEquals https://github.com/Microsoft/TypeScript/issues/13502
+ if (type !== undefined && checker.typeToString(type) === checker.symbolToString(symbol)) {
+ return true;
+ }
+ return ts.forEachChild(child, cb);
+ });
+}
+function getImplicitType(node, checker) {
+ if ((utils.isPropertyDeclaration(node) || utils.isVariableDeclaration(node)) && node.type === undefined) {
+ return checker.getTypeAtLocation(node);
+ }
+ else if (utils.isSignatureDeclaration(node) && node.type === undefined) {
+ var sig = checker.getSignatureFromDeclaration(node);
+ return sig === undefined ? undefined : sig.getReturnType();
+ }
+ else {
+ return undefined;
+ }
+}
+function isImportLike(node) {
+ return node.kind === ts.SyntaxKind.ImportDeclaration || node.kind === ts.SyntaxKind.ImportEqualsDeclaration;
+}
+function forEachImport(sourceFile, f) {
+ return ts.forEachChild(sourceFile, function (child) {
+ if (isImportLike(child)) {
+ var res = f(child);
+ if (res !== undefined) {
+ return res;
+ }
+ }
+ return undefined;
+ });
+}
+function findImport(pos, sourceFile) {
+ return forEachImport(sourceFile, function (i) {
+ if (i.kind === ts.SyntaxKind.ImportEqualsDeclaration) {
+ if (i.name.getStart() === pos) {
+ return i.name;
+ }
+ }
+ else {
+ if (i.importClause === undefined) {
+ // Error node
+ return undefined;
+ }
+ var _a = i.importClause, defaultName = _a.name, namedBindings = _a.namedBindings;
+ if (namedBindings !== undefined && namedBindings.kind === ts.SyntaxKind.NamespaceImport) {
+ var name = namedBindings.name;
+ if (name.getStart() === pos) {
+ return name;
+ }
+ return undefined;
+ }
+ if (defaultName !== undefined && defaultName.getStart() === pos) {
+ return defaultName;
+ }
+ else if (namedBindings !== undefined) {
+ for (var _i = 0, _b = namedBindings.elements; _i < _b.length; _i++) {
+ var name = _b[_i].name;
+ if (name.getStart() === pos) {
+ return name;
+ }
+ }
+ }
+ }
+ return undefined;
+ });
+}
+function getUnusedDiagnostic(diag) {
+ switch (diag.code) {
+ case 6133:
+ return 0 /* VARIABLE_OR_PARAMETER */; // "'{0}' is declared but never used.
+ case 6138:
+ return 1 /* PROPERTY */; // "Property '{0}' is declared but never used."
+ default:
+ return undefined;
+ }
+}
+var programToUnusedCheckedProgram = new WeakMap();
+function getUnusedCheckedProgram(program, checkParameters) {
+ // Assuming checkParameters will always have the same value, so only lookup by program.
+ var checkedProgram = programToUnusedCheckedProgram.get(program);
+ if (checkedProgram !== undefined) {
+ return checkedProgram;
+ }
+ checkedProgram = makeUnusedCheckedProgram(program, checkParameters);
+ programToUnusedCheckedProgram.set(program, checkedProgram);
+ return checkedProgram;
+}
+function makeUnusedCheckedProgram(program, checkParameters) {
+ var options = tslib_1.__assign({}, program.getCompilerOptions(), { noUnusedLocals: true }, (checkParameters ? { noUnusedParameters: true } : null));
+ var sourceFilesByName = new Map(program.getSourceFiles().map(function (s) { return [s.fileName, s]; }));
+ // tslint:disable object-literal-sort-keys
+ return ts.createProgram(Array.from(sourceFilesByName.keys()), options, {
+ fileExists: function (f) { return sourceFilesByName.has(f); },
+ readFile: function (f) {
+ var s = sourceFilesByName.get(f);
+ return s.text;
+ },
+ getSourceFile: function (f) { return sourceFilesByName.get(f); },
+ getDefaultLibFileName: function () { return ts.getDefaultLibFileName(options); },
+ writeFile: function () { },
+ getCurrentDirectory: function () { return ""; },
+ getDirectories: function () { return []; },
+ getCanonicalFileName: function (f) { return f; },
+ useCaseSensitiveFileNames: function () { return true; },
+ getNewLine: function () { return "\n"; },
+ });
+ // tslint:enable object-literal-sort-keys
+}
+var _a, _b;