1 | "use strict";
|
2 | var __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 | }));
|
9 | var __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 | });
|
14 | var __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 | };
|
21 | Object.defineProperty(exports, "__esModule", { value: true });
|
22 | const utils_1 = require("@typescript-eslint/utils");
|
23 | const ts = __importStar(require("typescript"));
|
24 | const util = __importStar(require("../util"));
|
25 | class 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 |
|
47 | if (checker.isArrayType(type)) {
|
48 | restType = {
|
49 | type: checker.getTypeArguments(type)[0],
|
50 | kind: 0 ,
|
51 | index: i,
|
52 | };
|
53 | }
|
54 | else if (checker.isTupleType(type)) {
|
55 | restType = {
|
56 | typeArguments: checker.getTypeArguments(type),
|
57 | kind: 1 ,
|
58 | index: i,
|
59 | };
|
60 | }
|
61 | else {
|
62 | restType = {
|
63 | type,
|
64 | kind: 2 ,
|
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 : {
|
83 | const typeArguments = this.restType.typeArguments;
|
84 | if (this.hasConsumedArguments) {
|
85 |
|
86 |
|
87 |
|
88 |
|
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 :
|
98 | case 2 :
|
99 | return this.restType.type;
|
100 | }
|
101 | }
|
102 | return this.paramTypes[index];
|
103 | }
|
104 | consumeRemainingArguments() {
|
105 | this.hasConsumedArguments = true;
|
106 | }
|
107 | }
|
108 | exports.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 |
|
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 |
|
147 | case utils_1.AST_NODE_TYPES.SpreadElement: {
|
148 | const spreadArgType = checker.getTypeAtLocation(esTreeNodeToTSNodeMap.get(argument.argument));
|
149 | if (util.isTypeAnyType(spreadArgType)) {
|
150 |
|
151 | context.report({
|
152 | node: argument,
|
153 | messageId: 'unsafeSpread',
|
154 | });
|
155 | }
|
156 | else if (util.isTypeAnyArrayType(spreadArgType, checker)) {
|
157 |
|
158 |
|
159 | context.report({
|
160 | node: argument,
|
161 | messageId: 'unsafeArraySpread',
|
162 | });
|
163 | }
|
164 | else if (checker.isTupleType(spreadArgType)) {
|
165 |
|
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 |
|
175 |
|
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 |
|
190 |
|
191 | signature.consumeRemainingArguments();
|
192 | }
|
193 | }
|
194 | else {
|
195 |
|
196 |
|
197 |
|
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 |
|
\ | No newline at end of file |