.gitignore added
[dotfiles/.git] / .config / coc / extensions / node_modules / coc-prettier / node_modules / tslint / lib / rules / noUnusedVariableRule.js
1 "use strict";
2 /**
3  * @license
4  * Copyright 2014 Palantir Technologies, Inc.
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18 Object.defineProperty(exports, "__esModule", { value: true });
19 var tslib_1 = require("tslib");
20 var semver = require("semver");
21 var utils = require("tsutils");
22 var ts = require("typescript");
23 var Lint = require("../index");
24 var OPTION_CHECK_PARAMETERS = "check-parameters";
25 var OPTION_IGNORE_PATTERN = "ignore-pattern";
26 var Rule = /** @class */ (function (_super) {
27     tslib_1.__extends(Rule, _super);
28     function Rule() {
29         return _super !== null && _super.apply(this, arguments) || this;
30     }
31     /* tslint:enable:object-literal-sort-keys */
32     Rule.prototype.applyWithProgram = function (sourceFile, program) {
33         return this.applyWithFunction(sourceFile, walk, parseOptions(this.ruleArguments), program);
34     };
35     /* tslint:disable:object-literal-sort-keys */
36     Rule.metadata = {
37         ruleName: "no-unused-variable",
38         description: Lint.Utils.dedent(templateObject_1 || (templateObject_1 = tslib_1.__makeTemplateObject(["\n            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."], ["\n            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."]))),
39         descriptionDetails: Lint.Utils.dedent(templateObject_2 || (templateObject_2 = tslib_1.__makeTemplateObject(["\n            In addition to avoiding compilation errors, this rule may still be useful if you\n            wish to have `tslint` automatically remove unused imports, variables, functions,\n            and private class members, when using TSLint's `--fix` option."], ["\n            In addition to avoiding compilation errors, this rule may still be useful if you\n            wish to have \\`tslint\\` automatically remove unused imports, variables, functions,\n            and private class members, when using TSLint's \\`--fix\\` option."]))),
40         hasFix: true,
41         optionsDescription: Lint.Utils.dedent(templateObject_3 || (templateObject_3 = tslib_1.__makeTemplateObject(["\n            Two 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 and imports that match the pattern will be ignored."], ["\n            Two 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 and imports that match the pattern will be ignored."]))),
42         options: {
43             type: "array",
44             items: {
45                 oneOf: [
46                     {
47                         type: "string",
48                         enum: ["check-parameters"],
49                     },
50                     {
51                         type: "object",
52                         properties: {
53                             "ignore-pattern": { type: "string" },
54                         },
55                         additionalProperties: false,
56                     },
57                 ],
58             },
59             minLength: 0,
60             maxLength: 2,
61         },
62         optionExamples: [true, [true, { "ignore-pattern": "^_" }]],
63         rationale: Lint.Utils.dedent(templateObject_4 || (templateObject_4 = tslib_1.__makeTemplateObject(["\n            Variables that are declared and not used anywhere in code are likely an error due to incomplete refactoring.\n            Such variables take up space in the code, are mild performance pains, and can lead to confusion by readers.\n        "], ["\n            Variables that are declared and not used anywhere in code are likely an error due to incomplete refactoring.\n            Such variables take up space in the code, are mild performance pains, and can lead to confusion by readers.\n        "]))),
64         type: "functionality",
65         typescriptOnly: true,
66         requiresTypeInfo: true,
67         deprecationMessage: semver.gte(ts.version, "2.9.0-dev.0")
68             ? "Since TypeScript 2.9. Please use the built-in compiler checks instead."
69             : undefined,
70     };
71     return Rule;
72 }(Lint.Rules.TypedRule));
73 exports.Rule = Rule;
74 function parseOptions(options) {
75     var checkParameters = options.indexOf(OPTION_CHECK_PARAMETERS) !== -1;
76     var ignorePattern;
77     for (var _i = 0, options_1 = options; _i < options_1.length; _i++) {
78         var o = options_1[_i];
79         if (typeof o === "object") {
80             // tslint:disable-next-line no-unsafe-any no-null-undefined-union
81             var ignore = o[OPTION_IGNORE_PATTERN];
82             if (ignore != undefined) {
83                 ignorePattern = new RegExp(ignore);
84                 break;
85             }
86         }
87     }
88     return { checkParameters: checkParameters, ignorePattern: ignorePattern };
89 }
90 function walk(ctx, program) {
91     var sourceFile = ctx.sourceFile, _a = ctx.options, checkParameters = _a.checkParameters, ignorePattern = _a.ignorePattern;
92     var unusedCheckedProgram = getUnusedCheckedProgram(program, checkParameters);
93     var diagnostics = ts.getPreEmitDiagnostics(unusedCheckedProgram, sourceFile);
94     var checker = unusedCheckedProgram.getTypeChecker(); // Doesn't matter which program is used for this.
95     var declaration = program.getCompilerOptions().declaration;
96     // If all specifiers in an import are unused, we elide the entire import.
97     var importSpecifierFailures = new Map();
98     for (var _i = 0, diagnostics_1 = diagnostics; _i < diagnostics_1.length; _i++) {
99         var diag = diagnostics_1[_i];
100         if (diag.start === undefined) {
101             continue;
102         }
103         var kind = getUnusedDiagnostic(diag);
104         if (kind === undefined) {
105             continue;
106         }
107         var failure = ts.flattenDiagnosticMessageText(diag.messageText, "\n");
108         // BUG: this means imports / destructures with all (2+) unused variables don't respect ignore pattern
109         if (ignorePattern !== undefined &&
110             kind !== 2 /* DECLARATION */ &&
111             kind !== 3 /* ALL_DESTRUCTURES */) {
112             var varName = /'(.*)'/.exec(failure)[1];
113             if (ignorePattern.test(varName)) {
114                 continue;
115             }
116         }
117         if (kind === 0 /* VARIABLE_OR_PARAMETER */ || kind === 2 /* DECLARATION */) {
118             var importNames = findImports(diag.start, sourceFile, kind);
119             if (importNames.length > 0) {
120                 for (var _b = 0, importNames_1 = importNames; _b < importNames_1.length; _b++) {
121                     var importName = importNames_1[_b];
122                     if (declaration && isImportUsed(importName, sourceFile, checker)) {
123                         continue;
124                     }
125                     if (importSpecifierFailures.has(importName)) {
126                         throw new Error("Should not get 2 errors for the same import.");
127                     }
128                     importSpecifierFailures.set(importName, failure);
129                 }
130                 continue;
131             }
132         }
133         ctx.addFailureAt(diag.start, diag.length, failure);
134     }
135     if (importSpecifierFailures.size !== 0) {
136         addImportSpecifierFailures(ctx, importSpecifierFailures, sourceFile);
137     }
138 }
139 /**
140  * Handle import-specifier failures separately.
141  * - If all of the import specifiers in an import are unused, add a combined failure for them all.
142  * - Unused imports are fixable.
143  */
144 function addImportSpecifierFailures(ctx, failures, sourceFile) {
145     forEachImport(sourceFile, function (importNode) {
146         if (importNode.kind === ts.SyntaxKind.ImportEqualsDeclaration) {
147             tryRemoveAll(importNode.name);
148             return;
149         }
150         if (importNode.importClause === undefined) {
151             // Error node
152             return;
153         }
154         var _a = importNode.importClause, defaultName = _a.name, namedBindings = _a.namedBindings;
155         if (namedBindings !== undefined && namedBindings.kind === ts.SyntaxKind.NamespaceImport) {
156             tryRemoveAll(namedBindings.name);
157             return;
158         }
159         var allNamedBindingsAreFailures = namedBindings === undefined || namedBindings.elements.every(function (e) { return failures.has(e.name); });
160         if (namedBindings !== undefined && allNamedBindingsAreFailures) {
161             for (var _i = 0, _b = namedBindings.elements; _i < _b.length; _i++) {
162                 var e = _b[_i];
163                 failures.delete(e.name);
164             }
165         }
166         if ((defaultName === undefined || failures.has(defaultName)) &&
167             allNamedBindingsAreFailures) {
168             if (defaultName !== undefined) {
169                 failures.delete(defaultName);
170             }
171             removeAll(importNode, "All imports on this line are unused.");
172             return;
173         }
174         if (defaultName !== undefined) {
175             var failure = tryDelete(defaultName);
176             if (failure !== undefined) {
177                 var start = defaultName.getStart();
178                 var end = namedBindings !== undefined
179                     ? namedBindings.getStart()
180                     : importNode.moduleSpecifier.getStart();
181                 var fix = Lint.Replacement.deleteFromTo(start, end);
182                 ctx.addFailureAtNode(defaultName, failure, fix);
183             }
184         }
185         if (namedBindings !== undefined) {
186             if (allNamedBindingsAreFailures) {
187                 var start = defaultName !== undefined ? defaultName.getEnd() : namedBindings.getStart();
188                 var fix = Lint.Replacement.deleteFromTo(start, namedBindings.getEnd());
189                 var failure = "All named bindings are unused.";
190                 ctx.addFailureAtNode(namedBindings, failure, fix);
191             }
192             else {
193                 var elements = namedBindings.elements;
194                 for (var i = 0; i < elements.length; i++) {
195                     var element = elements[i];
196                     var failure = tryDelete(element.name);
197                     if (failure === undefined) {
198                         continue;
199                     }
200                     var prevElement = elements[i - 1];
201                     var nextElement = elements[i + 1];
202                     var start = prevElement !== undefined ? prevElement.getEnd() : element.getStart();
203                     var end = nextElement !== undefined && prevElement == undefined
204                         ? nextElement.getStart()
205                         : element.getEnd();
206                     var fix = Lint.Replacement.deleteFromTo(start, end);
207                     ctx.addFailureAtNode(element.name, failure, fix);
208                 }
209             }
210         }
211         function tryRemoveAll(name) {
212             var failure = tryDelete(name);
213             if (failure !== undefined) {
214                 removeAll(name, failure);
215             }
216         }
217         function removeAll(errorNode, failure) {
218             var start = importNode.getStart();
219             var end = importNode.getEnd();
220             utils.forEachToken(importNode, function (token) {
221                 ts.forEachTrailingCommentRange(ctx.sourceFile.text, token.end, function (_, commentEnd, __) {
222                     end = commentEnd;
223                 });
224             }, ctx.sourceFile);
225             if (isEntireLine(start, end)) {
226                 end = getNextLineStart(end);
227             }
228             var fix = Lint.Replacement.deleteFromTo(start, end);
229             ctx.addFailureAtNode(errorNode, failure, fix);
230         }
231         function isEntireLine(start, end) {
232             return (ctx.sourceFile.getLineAndCharacterOfPosition(start).character === 0 &&
233                 ctx.sourceFile.getLineEndOfPosition(end) === end);
234         }
235         function getNextLineStart(position) {
236             var nextLine = ctx.sourceFile.getLineAndCharacterOfPosition(position).line + 1;
237             var lineStarts = ctx.sourceFile.getLineStarts();
238             if (nextLine < lineStarts.length) {
239                 return lineStarts[nextLine];
240             }
241             else {
242                 return position;
243             }
244         }
245     });
246     if (failures.size !== 0) {
247         throw new Error("Should have revisited all import specifier failures.");
248     }
249     function tryDelete(name) {
250         var failure = failures.get(name);
251         if (failure !== undefined) {
252             failures.delete(name);
253             return failure;
254         }
255         return undefined;
256     }
257 }
258 /**
259  * Ignore this import if it's used as an implicit type somewhere.
260  * Workround for https://github.com/Microsoft/TypeScript/issues/9944
261  */
262 function isImportUsed(importSpecifier, sourceFile, checker) {
263     var importedSymbol = checker.getSymbolAtLocation(importSpecifier);
264     if (importedSymbol === undefined) {
265         return false;
266     }
267     var symbol = checker.getAliasedSymbol(importedSymbol);
268     if (!utils.isSymbolFlagSet(symbol, ts.SymbolFlags.Type)) {
269         return false;
270     }
271     return (ts.forEachChild(sourceFile, function cb(child) {
272         if (isImportLike(child)) {
273             return false;
274         }
275         var type = getImplicitType(child, checker);
276         // TODO: checker.typeEquals https://github.com/Microsoft/TypeScript/issues/13502
277         if (type !== undefined &&
278             checker.typeToString(type) === checker.symbolToString(symbol)) {
279             return true;
280         }
281         return ts.forEachChild(child, cb);
282     }) === true);
283 }
284 function getImplicitType(node, checker) {
285     if (((utils.isPropertyDeclaration(node) || utils.isVariableDeclaration(node)) &&
286         node.type === undefined &&
287         node.name.kind === ts.SyntaxKind.Identifier) ||
288         (utils.isBindingElement(node) && node.name.kind === ts.SyntaxKind.Identifier)) {
289         return checker.getTypeAtLocation(node);
290     }
291     else if (utils.isSignatureDeclaration(node) && node.type === undefined) {
292         var sig = checker.getSignatureFromDeclaration(node);
293         return sig === undefined ? undefined : sig.getReturnType();
294     }
295     else {
296         return undefined;
297     }
298 }
299 function isImportLike(node) {
300     return (node.kind === ts.SyntaxKind.ImportDeclaration ||
301         node.kind === ts.SyntaxKind.ImportEqualsDeclaration);
302 }
303 function forEachImport(sourceFile, f) {
304     return ts.forEachChild(sourceFile, function (child) {
305         if (isImportLike(child)) {
306             var res = f(child);
307             if (res !== undefined) {
308                 return res;
309             }
310         }
311         return undefined;
312     });
313 }
314 function findImports(pos, sourceFile, kind) {
315     var imports = forEachImport(sourceFile, function (i) {
316         if (!isInRange(i, pos)) {
317             return undefined;
318         }
319         if (i.kind === ts.SyntaxKind.ImportEqualsDeclaration) {
320             return [i.name];
321         }
322         else {
323             if (i.importClause === undefined) {
324                 // Error node
325                 return undefined;
326             }
327             var _a = i.importClause, defaultName = _a.name, namedBindings = _a.namedBindings;
328             if (namedBindings !== undefined &&
329                 namedBindings.kind === ts.SyntaxKind.NamespaceImport) {
330                 return [namedBindings.name];
331             }
332             // Starting from TS2.8, when all imports in an import node are not used,
333             // TS emits only 1 diagnostic object for the whole line as opposed
334             // to the previous behavior of outputting a diagnostic with kind == 6192
335             // (UnusedKind.VARIABLE_OR_PARAMETER) for every unused import.
336             // From TS2.8, in the case of none of the imports in a line being used,
337             // the single diagnostic TS outputs are different between the 1 import
338             // and 2+ imports cases:
339             // - 1 import in node:
340             //   - diagnostic has kind == 6133 (UnusedKind.VARIABLE_OR_PARAMETER)
341             //   - the text range is the whole node (`import { ... } from "..."`)
342             //     whereas pre-TS2.8, the text range was for the import node. so
343             //     `name.getStart()` won't equal `pos` like in pre-TS2.8
344             // - 2+ imports in node:
345             //   - diagnostic has kind == 6192 (UnusedKind.DECLARATION)
346             //   - we know that all of these are unused
347             if (kind === 2 /* DECLARATION */) {
348                 var imp = [];
349                 if (defaultName !== undefined) {
350                     imp.push(defaultName);
351                 }
352                 if (namedBindings !== undefined) {
353                     imp.push.apply(imp, namedBindings.elements.map(function (el) { return el.name; }));
354                 }
355                 return imp.length > 0 ? imp : undefined;
356             }
357             else if (defaultName !== undefined &&
358                 (isInRange(defaultName, pos) || namedBindings === undefined) // defaultName is the only option
359             ) {
360                 return [defaultName];
361             }
362             else if (namedBindings !== undefined) {
363                 if (namedBindings.elements.length === 1) {
364                     return [namedBindings.elements[0].name];
365                 }
366                 for (var _i = 0, _b = namedBindings.elements; _i < _b.length; _i++) {
367                     var element = _b[_i];
368                     if (isInRange(element, pos)) {
369                         return [element.name];
370                     }
371                 }
372             }
373         }
374         return undefined;
375     });
376     return imports !== undefined ? imports : [];
377 }
378 function isInRange(range, pos) {
379     return range.pos <= pos && range.end >= pos;
380 }
381 function getUnusedDiagnostic(diag) {
382     // https://github.com/Microsoft/TypeScript/blob/master/src/compiler/diagnosticMessages.json
383     switch (diag.code) {
384         case 6133: // Pre TS 2.9 "'{0}' is declared but never used.
385         // TS 2.9+ "'{0}' is declared but its value is never read."
386         case 6196: // TS 2.9+ "'{0}' is declared but never used."
387             return 0 /* VARIABLE_OR_PARAMETER */;
388         case 6138:
389             return 1 /* PROPERTY */; // "Property '{0}' is declared but never used."
390         case 6192:
391             return 2 /* DECLARATION */; // "All imports in import declaration are unused."
392         case 6198:
393             return 3 /* ALL_DESTRUCTURES */; // "All destructured elements are unused."
394         default:
395             return undefined;
396     }
397 }
398 var programToUnusedCheckedProgram = new WeakMap();
399 function getUnusedCheckedProgram(program, checkParameters) {
400     // Assuming checkParameters will always have the same value, so only lookup by program.
401     var checkedProgram = programToUnusedCheckedProgram.get(program);
402     if (checkedProgram !== undefined) {
403         return checkedProgram;
404     }
405     checkedProgram = makeUnusedCheckedProgram(program, checkParameters);
406     programToUnusedCheckedProgram.set(program, checkedProgram);
407     return checkedProgram;
408 }
409 function makeUnusedCheckedProgram(program, checkParameters) {
410     var originalOptions = program.getCompilerOptions();
411     var options = tslib_1.__assign({}, originalOptions, { noEmit: true, noUnusedLocals: true, noUnusedParameters: originalOptions.noUnusedParameters || checkParameters });
412     var sourceFilesByName = new Map(program
413         .getSourceFiles()
414         .map(function (s) { return [getCanonicalFileName(s.fileName), s]; }));
415     // tslint:disable object-literal-sort-keys
416     return ts.createProgram(Array.from(sourceFilesByName.keys()), options, {
417         fileExists: function (f) { return sourceFilesByName.has(getCanonicalFileName(f)); },
418         readFile: function (f) { return sourceFilesByName.get(getCanonicalFileName(f)).text; },
419         getSourceFile: function (f) { return sourceFilesByName.get(getCanonicalFileName(f)); },
420         getDefaultLibFileName: function () { return ts.getDefaultLibFileName(options); },
421         writeFile: function () { return undefined; },
422         getCurrentDirectory: function () { return ""; },
423         getDirectories: function () { return []; },
424         getCanonicalFileName: getCanonicalFileName,
425         useCaseSensitiveFileNames: function () { return ts.sys.useCaseSensitiveFileNames; },
426         getNewLine: function () { return "\n"; },
427     });
428     // tslint:enable object-literal-sort-keys
429     // We need to be careful with file system case sensitivity
430     function getCanonicalFileName(fileName) {
431         return ts.sys.useCaseSensitiveFileNames ? fileName : fileName.toLowerCase();
432     }
433 }
434 var templateObject_1, templateObject_2, templateObject_3, templateObject_4;