.gitignore added
[dotfiles/.git] / .config / coc / extensions / node_modules / coc-prettier / node_modules / tslint / lib / rules / unifiedSignaturesRule.js
1 "use strict";
2 /**
3  * @license
4  * Copyright 2017 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 utils = require("tsutils");
21 var ts = require("typescript");
22 var Lint = require("../index");
23 var utils_1 = require("../utils");
24 var adjacentOverloadSignaturesRule_1 = require("./adjacentOverloadSignaturesRule");
25 var Rule = /** @class */ (function (_super) {
26     tslib_1.__extends(Rule, _super);
27     function Rule() {
28         return _super !== null && _super.apply(this, arguments) || this;
29     }
30     /* tslint:enable:object-literal-sort-keys */
31     Rule.FAILURE_STRING_OMITTING_SINGLE_PARAMETER = function (otherLine) {
32         return Rule.FAILURE_STRING_START(otherLine) + " with an optional parameter.";
33     };
34     Rule.FAILURE_STRING_OMITTING_REST_PARAMETER = function (otherLine) {
35         return Rule.FAILURE_STRING_START(otherLine) + " with a rest parameter.";
36     };
37     Rule.FAILURE_STRING_SINGLE_PARAMETER_DIFFERENCE = function (otherLine, type1, type2) {
38         return Rule.FAILURE_STRING_START(otherLine) + " taking `" + type1 + " | " + type2 + "`.";
39     };
40     Rule.FAILURE_STRING_START = function (otherLine) {
41         // For only 2 overloads we don't need to specify which is the other one.
42         var overloads = otherLine === undefined
43             ? "These overloads"
44             : "This overload and the one on line " + otherLine;
45         return overloads + " can be combined into one signature";
46     };
47     Rule.prototype.apply = function (sourceFile) {
48         return this.applyWithFunction(sourceFile, walk);
49     };
50     /* tslint:disable:object-literal-sort-keys */
51     Rule.metadata = {
52         ruleName: "unified-signatures",
53         description: "Warns for any two overloads that could be unified into one by using a union or an optional/rest parameter.",
54         optionsDescription: "Not configurable.",
55         options: null,
56         optionExamples: [true],
57         type: "typescript",
58         typescriptOnly: true,
59     };
60     return Rule;
61 }(Lint.Rules.AbstractRule));
62 exports.Rule = Rule;
63 function walk(ctx) {
64     var sourceFile = ctx.sourceFile;
65     checkStatements(sourceFile.statements);
66     return ts.forEachChild(sourceFile, function cb(node) {
67         switch (node.kind) {
68             case ts.SyntaxKind.ModuleBlock:
69                 checkStatements(node.statements);
70                 break;
71             case ts.SyntaxKind.InterfaceDeclaration:
72             case ts.SyntaxKind.ClassDeclaration: {
73                 var _a = node, members = _a.members, typeParameters = _a.typeParameters;
74                 checkMembers(members, typeParameters);
75                 break;
76             }
77             case ts.SyntaxKind.TypeLiteral:
78                 checkMembers(node.members);
79         }
80         return ts.forEachChild(node, cb);
81     });
82     function checkStatements(statements) {
83         addFailures(checkOverloads(statements, undefined, function (statement) {
84             if (utils.isFunctionDeclaration(statement)) {
85                 var body = statement.body, name = statement.name;
86                 return body === undefined && name !== undefined
87                     ? { signature: statement, key: name.text }
88                     : undefined;
89             }
90             else {
91                 return undefined;
92             }
93         }));
94     }
95     function checkMembers(members, typeParameters) {
96         addFailures(checkOverloads(members, typeParameters, function (member) {
97             switch (member.kind) {
98                 case ts.SyntaxKind.CallSignature:
99                 case ts.SyntaxKind.ConstructSignature:
100                 case ts.SyntaxKind.MethodSignature:
101                     break;
102                 case ts.SyntaxKind.MethodDeclaration:
103                 case ts.SyntaxKind.Constructor:
104                     if (member.body !==
105                         undefined) {
106                         return undefined;
107                     }
108                     break;
109                 default:
110                     return undefined;
111             }
112             var signature = member;
113             var key = adjacentOverloadSignaturesRule_1.getOverloadKey(signature);
114             return key === undefined ? undefined : { signature: signature, key: key };
115         }));
116     }
117     function addFailures(failures) {
118         for (var _i = 0, failures_1 = failures; _i < failures_1.length; _i++) {
119             var failure = failures_1[_i];
120             var unify = failure.unify, only2 = failure.only2;
121             switch (unify.kind) {
122                 case "single-parameter-difference": {
123                     var p0 = unify.p0, p1 = unify.p1;
124                     var lineOfOtherOverload = only2 ? undefined : getLine(p0.getStart());
125                     ctx.addFailureAtNode(p1, Rule.FAILURE_STRING_SINGLE_PARAMETER_DIFFERENCE(lineOfOtherOverload, typeText(p0), typeText(p1)));
126                     break;
127                 }
128                 case "extra-parameter": {
129                     var extraParameter = unify.extraParameter, otherSignature = unify.otherSignature;
130                     var lineOfOtherOverload = only2 ? undefined : getLine(otherSignature.pos);
131                     ctx.addFailureAtNode(extraParameter, extraParameter.dotDotDotToken !== undefined
132                         ? Rule.FAILURE_STRING_OMITTING_REST_PARAMETER(lineOfOtherOverload)
133                         : Rule.FAILURE_STRING_OMITTING_SINGLE_PARAMETER(lineOfOtherOverload));
134                 }
135             }
136         }
137     }
138     function getLine(pos) {
139         return ts.getLineAndCharacterOfPosition(sourceFile, pos).line + 1;
140     }
141 }
142 function checkOverloads(signatures, typeParameters, getOverload) {
143     var result = [];
144     var isTypeParameter = getIsTypeParameter(typeParameters);
145     for (var _i = 0, _a = collectOverloads(signatures, getOverload); _i < _a.length; _i++) {
146         var overloads = _a[_i];
147         if (overloads.length === 2) {
148             var unify = compareSignatures(overloads[0], overloads[1], isTypeParameter);
149             if (unify !== undefined) {
150                 result.push({ unify: unify, only2: true });
151             }
152         }
153         else {
154             forEachPair(overloads, function (a, b) {
155                 var unify = compareSignatures(a, b, isTypeParameter);
156                 if (unify !== undefined) {
157                     result.push({ unify: unify, only2: false });
158                 }
159             });
160         }
161     }
162     return result;
163 }
164 function compareSignatures(a, b, isTypeParameter) {
165     if (!signaturesCanBeUnified(a, b, isTypeParameter)) {
166         return undefined;
167     }
168     return a.parameters.length === b.parameters.length
169         ? signaturesDifferBySingleParameter(a.parameters, b.parameters)
170         : signaturesDifferByOptionalOrRestParameter(a.parameters, b.parameters);
171 }
172 function signaturesCanBeUnified(a, b, isTypeParameter) {
173     // Must return the same type.
174     return (typesAreEqual(a.type, b.type) &&
175         // Must take the same type parameters.
176         utils_1.arraysAreEqual(a.typeParameters, b.typeParameters, typeParametersAreEqual) &&
177         // If one uses a type parameter (from outside) and the other doesn't, they shouldn't be joined.
178         signatureUsesTypeParameter(a, isTypeParameter) ===
179             signatureUsesTypeParameter(b, isTypeParameter));
180 }
181 /** Detect `a(x: number, y: number, z: number)` and `a(x: number, y: string, z: number)`. */
182 function signaturesDifferBySingleParameter(types1, types2) {
183     var index = getIndexOfFirstDifference(types1, types2, parametersAreEqual);
184     if (index === undefined) {
185         return undefined;
186     }
187     // If remaining arrays are equal, the signatures differ by just one parameter type
188     if (!utils_1.arraysAreEqual(types1.slice(index + 1), types2.slice(index + 1), parametersAreEqual)) {
189         return undefined;
190     }
191     var a = types1[index];
192     var b = types2[index];
193     // Can unify `a?: string` and `b?: number`. Can't unify `...args: string[]` and `...args: number[]`.
194     // See https://github.com/Microsoft/TypeScript/issues/5077
195     return parametersHaveEqualSigils(a, b) && a.dotDotDotToken === undefined
196         ? { kind: "single-parameter-difference", p0: a, p1: b }
197         : undefined;
198 }
199 /**
200  * Detect `a(): void` and `a(x: number): void`.
201  * Returns the parameter declaration (`x: number` in this example) that should be optional/rest, and overload it's a part of.
202  */
203 function signaturesDifferByOptionalOrRestParameter(sig1, sig2) {
204     var minLength = Math.min(sig1.length, sig2.length);
205     var longer = sig1.length < sig2.length ? sig2 : sig1;
206     var shorter = sig1.length < sig2.length ? sig1 : sig2;
207     // If one is has 2+ parameters more than the other, they must all be optional/rest.
208     // Differ by optional parameters: f() and f(x), f() and f(x, ?y, ...z)
209     // Not allowed: f() and f(x, y)
210     for (var i = minLength + 1; i < longer.length; i++) {
211         if (!parameterMayBeMissing(longer[i])) {
212             return undefined;
213         }
214     }
215     for (var i = 0; i < minLength; i++) {
216         if (!typesAreEqual(sig1[i].type, sig2[i].type)) {
217             return undefined;
218         }
219     }
220     if (minLength > 0 && shorter[minLength - 1].dotDotDotToken !== undefined) {
221         return undefined;
222     }
223     return {
224         extraParameter: longer[longer.length - 1],
225         kind: "extra-parameter",
226         otherSignature: shorter,
227     };
228 }
229 /** Given type parameters, returns a function to test whether a type is one of those parameters. */
230 function getIsTypeParameter(typeParameters) {
231     if (typeParameters === undefined) {
232         return function () { return false; };
233     }
234     var set = new Set();
235     for (var _i = 0, typeParameters_1 = typeParameters; _i < typeParameters_1.length; _i++) {
236         var t = typeParameters_1[_i];
237         set.add(t.getText());
238     }
239     return function (typeName) { return set.has(typeName); };
240 }
241 /** True if any of the outer type parameters are used in a signature. */
242 function signatureUsesTypeParameter(sig, isTypeParameter) {
243     return sig.parameters.some(function (p) { return p.type !== undefined && typeContainsTypeParameter(p.type) === true; });
244     function typeContainsTypeParameter(type) {
245         if (utils.isTypeReferenceNode(type)) {
246             var typeName = type.typeName;
247             if (typeName.kind === ts.SyntaxKind.Identifier && isTypeParameter(typeName.text)) {
248                 return true;
249             }
250         }
251         return ts.forEachChild(type, typeContainsTypeParameter);
252     }
253 }
254 /**
255  * Given all signatures, collects an array of arrays of signatures which are all overloads.
256  * Does not rely on overloads being adjacent. This is similar to code in adjacentOverloadSignaturesRule.ts, but not the same.
257  */
258 function collectOverloads(nodes, getOverload) {
259     var map = new Map();
260     for (var _i = 0, nodes_1 = nodes; _i < nodes_1.length; _i++) {
261         var sig = nodes_1[_i];
262         var overload = getOverload(sig);
263         if (overload === undefined) {
264             continue;
265         }
266         var signature = overload.signature, key = overload.key;
267         var overloads = map.get(key);
268         if (overloads !== undefined) {
269             overloads.push(signature);
270         }
271         else {
272             map.set(key, [signature]);
273         }
274     }
275     return Array.from(map.values());
276 }
277 function parametersAreEqual(a, b) {
278     return parametersHaveEqualSigils(a, b) && typesAreEqual(a.type, b.type);
279 }
280 /** True for optional/rest parameters. */
281 function parameterMayBeMissing(p) {
282     return p.dotDotDotToken !== undefined || p.questionToken !== undefined;
283 }
284 /** False if one is optional and the other isn't, or one is a rest parameter and the other isn't. */
285 function parametersHaveEqualSigils(a, b) {
286     return ((a.dotDotDotToken !== undefined) === (b.dotDotDotToken !== undefined) &&
287         (a.questionToken !== undefined) === (b.questionToken !== undefined));
288 }
289 function typeParametersAreEqual(a, b) {
290     return a.name.text === b.name.text && typesAreEqual(a.constraint, b.constraint);
291 }
292 function typesAreEqual(a, b) {
293     // TODO: Could traverse AST so that formatting differences don't affect this.
294     return a === b || (a !== undefined && b !== undefined && a.getText() === b.getText());
295 }
296 /** Returns the first index where `a` and `b` differ. */
297 function getIndexOfFirstDifference(a, b, equal) {
298     for (var i = 0; i < a.length && i < b.length; i++) {
299         if (!equal(a[i], b[i])) {
300             return i;
301         }
302     }
303     return undefined;
304 }
305 /** Calls `action` for every pair of values in `values`. */
306 function forEachPair(values, action) {
307     for (var i = 0; i < values.length; i++) {
308         for (var j = i + 1; j < values.length; j++) {
309             var result = action(values[i], values[j]);
310             if (result !== undefined) {
311                 return result;
312             }
313         }
314     }
315     return undefined;
316 }
317 function typeText(_a) {
318     var type = _a.type;
319     return type === undefined ? "any" : type.getText();
320 }