| "use strict"; |
| var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { |
| if (k2 === undefined) k2 = k; |
| Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); |
| }) : (function(o, m, k, k2) { |
| if (k2 === undefined) k2 = k; |
| o[k2] = m[k]; |
| })); |
| var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { |
| Object.defineProperty(o, "default", { enumerable: true, value: v }); |
| }) : function(o, v) { |
| o["default"] = v; |
| }); |
| var __importStar = (this && this.__importStar) || function (mod) { |
| if (mod && mod.__esModule) return mod; |
| var result = {}; |
| if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); |
| __setModuleDefault(result, mod); |
| return result; |
| }; |
| Object.defineProperty(exports, "__esModule", { value: true }); |
| const utils_1 = require("@typescript-eslint/utils"); |
| const ts = __importStar(require("typescript")); |
| const util = __importStar(require("../util")); |
| class FunctionSignature { |
| constructor(paramTypes, restType) { |
| this.paramTypes = paramTypes; |
| this.restType = restType; |
| this.parameterTypeIndex = 0; |
| this.hasConsumedArguments = false; |
| } |
| static create(checker, tsNode) { |
| var _a; |
| const signature = checker.getResolvedSignature(tsNode); |
| if (!signature) { |
| return null; |
| } |
| const paramTypes = []; |
| let restType = null; |
| const parameters = signature.getParameters(); |
| for (let i = 0; i < parameters.length; i += 1) { |
| const param = parameters[i]; |
| const type = checker.getTypeOfSymbolAtLocation(param, tsNode); |
| const decl = (_a = param.getDeclarations()) === null || _a === void 0 ? void 0 : _a[0]; |
| if (decl && ts.isParameter(decl) && decl.dotDotDotToken) { |
| // is a rest param |
| if (checker.isArrayType(type)) { |
| restType = { |
| type: checker.getTypeArguments(type)[0], |
| kind: 0 /* Array */, |
| index: i, |
| }; |
| } |
| else if (checker.isTupleType(type)) { |
| restType = { |
| typeArguments: checker.getTypeArguments(type), |
| kind: 1 /* Tuple */, |
| index: i, |
| }; |
| } |
| else { |
| restType = { |
| type, |
| kind: 2 /* Other */, |
| index: i, |
| }; |
| } |
| break; |
| } |
| paramTypes.push(type); |
| } |
| return new this(paramTypes, restType); |
| } |
| getNextParameterType() { |
| const index = this.parameterTypeIndex; |
| this.parameterTypeIndex += 1; |
| if (index >= this.paramTypes.length || this.hasConsumedArguments) { |
| if (this.restType == null) { |
| return null; |
| } |
| switch (this.restType.kind) { |
| case 1 /* Tuple */: { |
| const typeArguments = this.restType.typeArguments; |
| if (this.hasConsumedArguments) { |
| // all types consumed by a rest - just assume it's the last type |
| // there is one edge case where this is wrong, but we ignore it because |
| // it's rare and really complicated to handle |
| // eg: function foo(...a: [number, ...string[], number]) |
| return typeArguments[typeArguments.length - 1]; |
| } |
| const typeIndex = index - this.restType.index; |
| if (typeIndex >= typeArguments.length) { |
| return typeArguments[typeArguments.length - 1]; |
| } |
| return typeArguments[typeIndex]; |
| } |
| case 0 /* Array */: |
| case 2 /* Other */: |
| return this.restType.type; |
| } |
| } |
| return this.paramTypes[index]; |
| } |
| consumeRemainingArguments() { |
| this.hasConsumedArguments = true; |
| } |
| } |
| exports.default = util.createRule({ |
| name: 'no-unsafe-argument', |
| meta: { |
| type: 'problem', |
| docs: { |
| description: 'Disallows calling a function with an any type value', |
| recommended: 'error', |
| requiresTypeChecking: true, |
| }, |
| messages: { |
| unsafeArgument: 'Unsafe argument of type `{{sender}}` assigned to a parameter of type `{{receiver}}`.', |
| unsafeTupleSpread: 'Unsafe spread of a tuple type. The {{index}} element is of type `{{sender}}` and is assigned to a parameter of type `{{reciever}}`.', |
| unsafeArraySpread: 'Unsafe spread of an `any` array type.', |
| unsafeSpread: 'Unsafe spread of an `any` type.', |
| }, |
| schema: [], |
| }, |
| defaultOptions: [], |
| create(context) { |
| const { program, esTreeNodeToTSNodeMap } = util.getParserServices(context); |
| const checker = program.getTypeChecker(); |
| return { |
| 'CallExpression, NewExpression'(node) { |
| if (node.arguments.length === 0) { |
| return; |
| } |
| // ignore any-typed calls as these are caught by no-unsafe-call |
| if (util.isTypeAnyType(checker.getTypeAtLocation(esTreeNodeToTSNodeMap.get(node.callee)))) { |
| return; |
| } |
| const tsNode = esTreeNodeToTSNodeMap.get(node); |
| const signature = FunctionSignature.create(checker, tsNode); |
| if (!signature) { |
| return; |
| } |
| for (let i = 0; i < node.arguments.length; i += 1) { |
| const argument = node.arguments[i]; |
| switch (argument.type) { |
| // spreads consume |
| case utils_1.AST_NODE_TYPES.SpreadElement: { |
| const spreadArgType = checker.getTypeAtLocation(esTreeNodeToTSNodeMap.get(argument.argument)); |
| if (util.isTypeAnyType(spreadArgType)) { |
| // foo(...any) |
| context.report({ |
| node: argument, |
| messageId: 'unsafeSpread', |
| }); |
| } |
| else if (util.isTypeAnyArrayType(spreadArgType, checker)) { |
| // foo(...any[]) |
| // TODO - we could break down the spread and compare the array type against each argument |
| context.report({ |
| node: argument, |
| messageId: 'unsafeArraySpread', |
| }); |
| } |
| else if (checker.isTupleType(spreadArgType)) { |
| // foo(...[tuple1, tuple2]) |
| const spreadTypeArguments = checker.getTypeArguments(spreadArgType); |
| for (let j = 0; j < spreadTypeArguments.length; j += 1) { |
| const tupleType = spreadTypeArguments[j]; |
| const parameterType = signature.getNextParameterType(); |
| if (parameterType == null) { |
| continue; |
| } |
| const result = util.isUnsafeAssignment(tupleType, parameterType, checker, |
| // we can't pass the individual tuple members in here as this will most likely be a spread variable |
| // not a spread array |
| null); |
| if (result) { |
| context.report({ |
| node: argument, |
| messageId: 'unsafeTupleSpread', |
| data: { |
| sender: checker.typeToString(tupleType), |
| receiver: checker.typeToString(parameterType), |
| }, |
| }); |
| } |
| } |
| if (spreadArgType.target.hasRestElement) { |
| // the last element was a rest - so all remaining defined arguments can be considered "consumed" |
| // all remaining arguments should be compared against the rest type (if one exists) |
| signature.consumeRemainingArguments(); |
| } |
| } |
| else { |
| // something that's iterable |
| // handling this will be pretty complex - so we ignore it for now |
| // TODO - handle generic iterable case |
| } |
| break; |
| } |
| default: { |
| const parameterType = signature.getNextParameterType(); |
| if (parameterType == null) { |
| continue; |
| } |
| const argumentType = checker.getTypeAtLocation(esTreeNodeToTSNodeMap.get(argument)); |
| const result = util.isUnsafeAssignment(argumentType, parameterType, checker, argument); |
| if (result) { |
| context.report({ |
| node: argument, |
| messageId: 'unsafeArgument', |
| data: { |
| sender: checker.typeToString(argumentType), |
| receiver: checker.typeToString(parameterType), |
| }, |
| }); |
| } |
| } |
| } |
| } |
| }, |
| }; |
| }, |
| }); |
| //# sourceMappingURL=no-unsafe-argument.js.map |