UNPKG

typir

Version:

General purpose type checking library

103 lines 6.09 kB
/****************************************************************************** * Copyright 2025 TypeFox GmbH * This program and the accompanying materials are made available under the * terms of the MIT License, which is available in the project root. ******************************************************************************/ import { isAssignabilityProblem } from '../../services/assignability.js'; import { InferenceProblem, InferenceRuleNotApplicable } from '../../services/inference.js'; import { checkTypeArrays } from '../../utils/utils-type-comparison.js'; /** * Dedicated inference rule for calls of a single function signature. * It ensures, that all parameters match, and provides information, how parameters are matching ('assignabilitySuccess'). * * Note: If multiple inference rules are configured for the same FunctionType, for each of these inference rules one instance of 'FunctionCallInferenceRule' is created, * since these inference rules are independent from each other (and only return the same FunctionType). * * Preconditions: * - there is a rule which specifies how to infer the current function type * - the current function has an output type/parameter, otherwise, this function could not provide any type (and throws an error), when it is called! * (exception: the options contain a type to return in this special case) */ export class FunctionCallInferenceRule { constructor(typeDetails, inferenceRuleForCalls, functionType, functions) { this.typeDetails = typeDetails; this.inferenceRuleForCalls = inferenceRuleForCalls; this.functionType = functionType; this.functions = functions; this.assignabilitySuccess = new Array(typeDetails.inputParameters.length); } inferTypeWithoutChildren(languageNode, _typir) { this.assignabilitySuccess.fill(undefined); // reset the entries // 0. The LanguageKeys are already checked by OverloadedFunctionsTypeInferenceRule, nothing to do here // 1. Does the filter of the inference rule accept the current language node? const result = this.inferenceRuleForCalls.filter === undefined || this.inferenceRuleForCalls.filter(languageNode); if (!result) { // the language node has a completely different purpose return InferenceRuleNotApplicable; } // 2. Does the inference rule match this language node? const matching = this.inferenceRuleForCalls.matching === undefined || this.inferenceRuleForCalls.matching(languageNode, this.functionType); if (!matching) { // the language node is slightly different return InferenceRuleNotApplicable; } // 3. Check whether the current arguments fit to the expected parameter types // 3a. Check some special cases, in order to save the effort to do type inference for the given arguments const overloadInfos = this.functions.getOverloads(this.typeDetails.functionName); if (overloadInfos === undefined || overloadInfos.overloadedFunctions.length <= 1) { // the current function is not overloaded, therefore, the types of their parameters are not required => save time, ignore inference errors return this.check(this.getOutputTypeForFunctionCalls()); } // two or more overloaded functions if (overloadInfos.sameOutputType) { // special case: all(!) overloaded functions have the same(!) output type, save performance and return this type! return overloadInfos.sameOutputType; } // 3b. The given arguments need to be checked in detail => infer the types of the parameters now const inputArguments = this.inferenceRuleForCalls.inputArguments(languageNode); return inputArguments; } inferTypeWithChildrensTypes(languageNode, actualInputTypes, typir) { const expectedInputTypes = this.typeDetails.inputParameters.map(p => typir.infrastructure.TypeResolver.resolve(p.type)); // all operands need to be assignable(! not equal) to the required types const comparisonConflicts = checkTypeArrays(actualInputTypes, expectedInputTypes, (t1, t2, index) => { const result = typir.Assignability.getAssignabilityResult(t1, t2); if (isAssignabilityProblem(result)) { return result; } else { // save the information equal/conversion/subtype for deciding "conflicts" of overloaded functions this.assignabilitySuccess[index] = result; return undefined; } }, true); if (comparisonConflicts.length >= 1) { // this function type does not match, due to assignability conflicts => return them as errors return { $problem: InferenceProblem, languageNode: languageNode, inferenceCandidate: this.functionType, location: 'input parameters', rule: this, subProblems: comparisonConflicts, }; // We have a dedicated validation for this case (see below), but a resulting error might be ignored by the user => return the problem during type-inference again } else { // matching => return the return type of the function for the case of a function call! return this.check(this.getOutputTypeForFunctionCalls()); } } getOutputTypeForFunctionCalls() { return this.functionType.kind.getOutputTypeForFunctionCalls(this.functionType); } check(returnType) { if (returnType) { // this condition is checked here, since 'undefined' is OK, as long as it is not used; extracting this function is difficult due to TypeScripts strict rules for using 'this' return returnType; } else { throw new Error(`The function ${this.typeDetails.functionName} is called, but has no output type to infer.`); } } } //# sourceMappingURL=function-inference-call.js.map