wallet-core/node_modules/tslint/lib/rules/noUnusedVariableRule.js

376 lines
17 KiB
JavaScript
Raw Normal View History

2017-05-28 00:38:50 +02:00
"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();
2017-08-14 05:01:11 +02:00
if (x.noUnusedLocals && x.noUnusedParameters) {
2017-05-28 00:38:50 +02:00
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)); });
};
2017-08-14 05:01:11 +02:00
/* 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,
2017-05-28 00:38:50 +02:00
},
2017-08-14 05:01:11 +02:00
],
},
minLength: 0,
maxLength: 3,
2017-05-28 00:38:50 +02:00
},
2017-08-14 05:01:11 +02:00
optionExamples: [true, [true, { "ignore-pattern": "^_" }]],
type: "functionality",
typescriptOnly: true,
requiresTypeInfo: true,
};
return Rule;
}(Lint.Rules.TypedRule));
2017-05-28 00:38:50 +02:00
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.
2017-08-14 05:01:11 +02:00
var declaration = program.getCompilerOptions().declaration;
2017-05-28 00:38:50 +02:00
// 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];
2017-08-14 05:01:11 +02:00
if (diag.start === undefined) {
continue;
}
2017-05-28 00:38:50 +02:00
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) {
2017-08-14 05:01:11 +02:00
if (declaration && isImportUsed(importName, sourceFile, checker)) {
2017-05-28 00:38:50 +02:00
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) {
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) {
2017-08-14 05:01:11 +02:00
var start = importNode.getStart();
var end = importNode.getEnd();
if (isEntireLine(start, end)) {
end = getNextLineStart(end);
}
var fix = Lint.Replacement.deleteFromTo(start, end);
2017-05-28 00:38:50 +02:00
ctx.addFailureAtNode(errorNode, failure, fix);
}
2017-08-14 05:01:11 +02:00
function isEntireLine(start, end) {
return ctx.sourceFile.getLineAndCharacterOfPosition(start).character === 0 &&
ctx.sourceFile.getLineEndOfPosition(end) === end;
}
function getNextLineStart(position) {
var nextLine = ctx.sourceFile.getLineAndCharacterOfPosition(position).line + 1;
var lineStarts = ctx.sourceFile.getLineStarts();
if (nextLine < lineStarts.length) {
return lineStarts[nextLine];
}
else {
return position;
}
}
2017-05-28 00:38:50 +02:00
});
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);
2017-08-14 05:01:11 +02:00
}) === true;
2017-05-28 00:38:50 +02:00
}
function getImplicitType(node, checker) {
2017-08-14 05:01:11 +02:00
if ((utils.isPropertyDeclaration(node) || utils.isVariableDeclaration(node)) &&
node.type === undefined && node.name.kind === ts.SyntaxKind.Identifier ||
utils.isBindingElement(node) && node.name.kind === ts.SyntaxKind.Identifier) {
2017-05-28 00:38:50 +02:00
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) {
2017-08-14 05:01:11 +02:00
var originalOptions = program.getCompilerOptions();
var options = tslib_1.__assign({}, originalOptions, { noEmit: true, noUnusedLocals: true, noUnusedParameters: originalOptions.noUnusedParameters || checkParameters });
var sourceFilesByName = new Map(program.getSourceFiles().map(function (s) { return [getCanonicalFileName(s.fileName), s]; }));
2017-05-28 00:38:50 +02:00
// tslint:disable object-literal-sort-keys
return ts.createProgram(Array.from(sourceFilesByName.keys()), options, {
2017-08-14 05:01:11 +02:00
fileExists: function (f) { return sourceFilesByName.has(getCanonicalFileName(f)); },
readFile: function (f) { return sourceFilesByName.get(getCanonicalFileName(f)).text; },
getSourceFile: function (f) { return sourceFilesByName.get(getCanonicalFileName(f)); },
2017-05-28 00:38:50 +02:00
getDefaultLibFileName: function () { return ts.getDefaultLibFileName(options); },
writeFile: function () { },
getCurrentDirectory: function () { return ""; },
getDirectories: function () { return []; },
2017-08-14 05:01:11 +02:00
getCanonicalFileName: getCanonicalFileName,
useCaseSensitiveFileNames: function () { return ts.sys.useCaseSensitiveFileNames; },
2017-05-28 00:38:50 +02:00
getNewLine: function () { return "\n"; },
});
// tslint:enable object-literal-sort-keys
2017-08-14 05:01:11 +02:00
// We need to be careful with file system case sensitivity
function getCanonicalFileName(fileName) {
return ts.sys.useCaseSensitiveFileNames ? fileName : fileName.toLowerCase();
}
2017-05-28 00:38:50 +02:00
}
var _a, _b;