diff options
Diffstat (limited to 'node_modules/tslint/lib/rules/noUnusedVariableRule.js')
-rw-r--r-- | node_modules/tslint/lib/rules/noUnusedVariableRule.js | 350 |
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; |