UNPKG

10.8 kBJavaScriptView Raw
1"use strict";
2var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3 if (k2 === undefined) k2 = k;
4 Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
5}) : (function(o, m, k, k2) {
6 if (k2 === undefined) k2 = k;
7 o[k2] = m[k];
8}));
9var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
10 Object.defineProperty(o, "default", { enumerable: true, value: v });
11}) : function(o, v) {
12 o["default"] = v;
13});
14var __importStar = (this && this.__importStar) || function (mod) {
15 if (mod && mod.__esModule) return mod;
16 var result = {};
17 if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
18 __setModuleDefault(result, mod);
19 return result;
20};
21Object.defineProperty(exports, "__esModule", { value: true });
22const utils_1 = require("@typescript-eslint/utils");
23const ts = __importStar(require("typescript"));
24const util = __importStar(require("../util"));
25class FunctionSignature {
26 constructor(paramTypes, restType) {
27 this.paramTypes = paramTypes;
28 this.restType = restType;
29 this.parameterTypeIndex = 0;
30 this.hasConsumedArguments = false;
31 }
32 static create(checker, tsNode) {
33 var _a;
34 const signature = checker.getResolvedSignature(tsNode);
35 if (!signature) {
36 return null;
37 }
38 const paramTypes = [];
39 let restType = null;
40 const parameters = signature.getParameters();
41 for (let i = 0; i < parameters.length; i += 1) {
42 const param = parameters[i];
43 const type = checker.getTypeOfSymbolAtLocation(param, tsNode);
44 const decl = (_a = param.getDeclarations()) === null || _a === void 0 ? void 0 : _a[0];
45 if (decl && ts.isParameter(decl) && decl.dotDotDotToken) {
46 // is a rest param
47 if (checker.isArrayType(type)) {
48 restType = {
49 type: checker.getTypeArguments(type)[0],
50 kind: 0 /* Array */,
51 index: i,
52 };
53 }
54 else if (checker.isTupleType(type)) {
55 restType = {
56 typeArguments: checker.getTypeArguments(type),
57 kind: 1 /* Tuple */,
58 index: i,
59 };
60 }
61 else {
62 restType = {
63 type,
64 kind: 2 /* Other */,
65 index: i,
66 };
67 }
68 break;
69 }
70 paramTypes.push(type);
71 }
72 return new this(paramTypes, restType);
73 }
74 getNextParameterType() {
75 const index = this.parameterTypeIndex;
76 this.parameterTypeIndex += 1;
77 if (index >= this.paramTypes.length || this.hasConsumedArguments) {
78 if (this.restType == null) {
79 return null;
80 }
81 switch (this.restType.kind) {
82 case 1 /* Tuple */: {
83 const typeArguments = this.restType.typeArguments;
84 if (this.hasConsumedArguments) {
85 // all types consumed by a rest - just assume it's the last type
86 // there is one edge case where this is wrong, but we ignore it because
87 // it's rare and really complicated to handle
88 // eg: function foo(...a: [number, ...string[], number])
89 return typeArguments[typeArguments.length - 1];
90 }
91 const typeIndex = index - this.restType.index;
92 if (typeIndex >= typeArguments.length) {
93 return typeArguments[typeArguments.length - 1];
94 }
95 return typeArguments[typeIndex];
96 }
97 case 0 /* Array */:
98 case 2 /* Other */:
99 return this.restType.type;
100 }
101 }
102 return this.paramTypes[index];
103 }
104 consumeRemainingArguments() {
105 this.hasConsumedArguments = true;
106 }
107}
108exports.default = util.createRule({
109 name: 'no-unsafe-argument',
110 meta: {
111 type: 'problem',
112 docs: {
113 description: 'Disallows calling a function with an any type value',
114 recommended: 'error',
115 requiresTypeChecking: true,
116 },
117 messages: {
118 unsafeArgument: 'Unsafe argument of type `{{sender}}` assigned to a parameter of type `{{receiver}}`.',
119 unsafeTupleSpread: 'Unsafe spread of a tuple type. The {{index}} element is of type `{{sender}}` and is assigned to a parameter of type `{{reciever}}`.',
120 unsafeArraySpread: 'Unsafe spread of an `any` array type.',
121 unsafeSpread: 'Unsafe spread of an `any` type.',
122 },
123 schema: [],
124 },
125 defaultOptions: [],
126 create(context) {
127 const { program, esTreeNodeToTSNodeMap } = util.getParserServices(context);
128 const checker = program.getTypeChecker();
129 return {
130 'CallExpression, NewExpression'(node) {
131 if (node.arguments.length === 0) {
132 return;
133 }
134 // ignore any-typed calls as these are caught by no-unsafe-call
135 if (util.isTypeAnyType(checker.getTypeAtLocation(esTreeNodeToTSNodeMap.get(node.callee)))) {
136 return;
137 }
138 const tsNode = esTreeNodeToTSNodeMap.get(node);
139 const signature = FunctionSignature.create(checker, tsNode);
140 if (!signature) {
141 return;
142 }
143 for (let i = 0; i < node.arguments.length; i += 1) {
144 const argument = node.arguments[i];
145 switch (argument.type) {
146 // spreads consume
147 case utils_1.AST_NODE_TYPES.SpreadElement: {
148 const spreadArgType = checker.getTypeAtLocation(esTreeNodeToTSNodeMap.get(argument.argument));
149 if (util.isTypeAnyType(spreadArgType)) {
150 // foo(...any)
151 context.report({
152 node: argument,
153 messageId: 'unsafeSpread',
154 });
155 }
156 else if (util.isTypeAnyArrayType(spreadArgType, checker)) {
157 // foo(...any[])
158 // TODO - we could break down the spread and compare the array type against each argument
159 context.report({
160 node: argument,
161 messageId: 'unsafeArraySpread',
162 });
163 }
164 else if (checker.isTupleType(spreadArgType)) {
165 // foo(...[tuple1, tuple2])
166 const spreadTypeArguments = checker.getTypeArguments(spreadArgType);
167 for (let j = 0; j < spreadTypeArguments.length; j += 1) {
168 const tupleType = spreadTypeArguments[j];
169 const parameterType = signature.getNextParameterType();
170 if (parameterType == null) {
171 continue;
172 }
173 const result = util.isUnsafeAssignment(tupleType, parameterType, checker,
174 // we can't pass the individual tuple members in here as this will most likely be a spread variable
175 // not a spread array
176 null);
177 if (result) {
178 context.report({
179 node: argument,
180 messageId: 'unsafeTupleSpread',
181 data: {
182 sender: checker.typeToString(tupleType),
183 receiver: checker.typeToString(parameterType),
184 },
185 });
186 }
187 }
188 if (spreadArgType.target.hasRestElement) {
189 // the last element was a rest - so all remaining defined arguments can be considered "consumed"
190 // all remaining arguments should be compared against the rest type (if one exists)
191 signature.consumeRemainingArguments();
192 }
193 }
194 else {
195 // something that's iterable
196 // handling this will be pretty complex - so we ignore it for now
197 // TODO - handle generic iterable case
198 }
199 break;
200 }
201 default: {
202 const parameterType = signature.getNextParameterType();
203 if (parameterType == null) {
204 continue;
205 }
206 const argumentType = checker.getTypeAtLocation(esTreeNodeToTSNodeMap.get(argument));
207 const result = util.isUnsafeAssignment(argumentType, parameterType, checker, argument);
208 if (result) {
209 context.report({
210 node: argument,
211 messageId: 'unsafeArgument',
212 data: {
213 sender: checker.typeToString(argumentType),
214 receiver: checker.typeToString(parameterType),
215 },
216 });
217 }
218 }
219 }
220 }
221 },
222 };
223 },
224});
225//# sourceMappingURL=no-unsafe-argument.js.map
\No newline at end of file