UNPKG

typir

Version:

General purpose type checking library

113 lines 6.08 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 { RuleRegistry } from '../../utils/rule-registration.js'; import { removeFromArray } from '../../utils/utils.js'; import { OverloadedFunctionsTypeInferenceRule } from './function-inference-overloaded.js'; import { isFunctionType } from './function-type.js'; import { FunctionCallArgumentsValidation } from './function-validation-calls.js'; /** * Contains all the logic to manage all available functions, * in particular, to support overloaded functions. * In each type system, exactly one instance of this class is stored by the FunctionKind. */ export class AvailableFunctionsManager { constructor(services, kind) { /** * function name => all overloaded functions (with additional information) with this name/key * - The types could be collected with the TypeGraphListener, but the additional information like inference rules are not available. * Therefore this map needs to be maintained here. * - Main purpose is to support inference and validation for overloaded functions: * Since overloaded functions are realized with one function type for each variant, * the corresponding rules and logic need to involve multiple types, * which makes it more complex and requires to manage them here and not in the single types. */ this.mapNameTypes = new Map(); this.services = services; this.kind = kind; this.services.infrastructure.Graph.addListener(this); // this validation rule for checking arguments of function calls exists "for ever", since it validates all function types this.validatorArgumentsCalls = this.createFunctionCallArgumentsValidation(); } createFunctionCallArgumentsValidation() { // since kind/map is required for the validation (but not visible to the outside), it is created here by the factory return new FunctionCallArgumentsValidation(this.services, this); } createInferenceRuleForOverloads() { // This inference rule don't need to be registered at the Inference service, since it manages the (de)registrations itself! return new OverloadedFunctionsTypeInferenceRule(this.services, this.services.Inference); } getOverloads(functionName) { return this.mapNameTypes.get(functionName); } getOrCreateOverloads(functionName) { let result = this.mapNameTypes.get(functionName); if (result === undefined) { result = { overloadedFunctions: [], details: new RuleRegistry(this.services), inferenceRule: this.createInferenceRuleForOverloads(), sameOutputType: undefined, }; this.mapNameTypes.set(functionName, result); // the "global" validation for function calls needs to update its registration according to added/removed inference rules for calls of added/removed functions result.details.addListener(this.validatorArgumentsCalls); } return result; } getAllOverloads() { return this.mapNameTypes.entries(); } addFunction(readyFunctionType, inferenceRulesForCalls) { const overloaded = this.getOrCreateOverloads(readyFunctionType.functionName); // remember the function type itself overloaded.overloadedFunctions.push(readyFunctionType); this.calculateSameOutputType(overloaded); // register each inference rule for calls of the function inferenceRulesForCalls.forEach(rule => overloaded.details.addRule({ functionType: readyFunctionType, inferenceRuleForCalls: rule, }, { languageKey: rule.languageKey, // the language keys are directly encoded inside these special inference rules for function calls boundToType: readyFunctionType, // these rules are specific for current function type/signature })); } /* Get informed about deleted types in order to remove inference rules which are bound to them. */ onRemovedType(type, _key) { if (isFunctionType(type)) { const overloaded = this.getOverloads(type.functionName); if (overloaded) { // remove the current function const removed = removeFromArray(type, overloaded.overloadedFunctions); if (removed) { this.calculateSameOutputType(overloaded); } // the rule registry removes this function type on its own => nothing to do here // its inference rule is removed by the CompositeTypeInferenceRule => nothing to do here } } } calculateSameOutputType(overloaded) { overloaded.sameOutputType = undefined; for (let index = 0; index < overloaded.overloadedFunctions.length; index++) { const current = overloaded.overloadedFunctions[index]; const outputTypeForFunctionCalls = this.kind.getOutputTypeForFunctionCalls(current); // output parameter for function calls if (index === 0) { overloaded.sameOutputType = outputTypeForFunctionCalls; } else { if (overloaded.sameOutputType && outputTypeForFunctionCalls && this.services.Equality.areTypesEqual(overloaded.sameOutputType, outputTypeForFunctionCalls) === true) { // the output types of all overloaded functions are the same for now } else { // there is a difference overloaded.sameOutputType = undefined; break; } } } } } //# sourceMappingURL=function-overloading.js.map