GenericSubstitutions (@Nullable PyExpression receiver, @NotNull Map<PyExpression, PyCallableParameter> arguments, @NotNull TypeEvalContext context) { // UnifyReceiver retains the information about type parameters of ancestor generic classes, // which might be necessary if a method being called is defined in one them, not in the actual // class of the receiver. In theory, it could be replaced by matching the type of self parameter // with the receiver uniformly with the rest of the arguments. final var substitutions = unifyReceiver(receiver, context); for (Map.Entry<PyExpression, PyCallableParameter> entry : getRegularMappedParameters(arguments).entrySet()) { final PyCallableParameter paramWrapper = entry.getValue(); final PyType expectedType = paramWrapper.getArgumentType(context); final PyType promotedToLiteral = PyLiteralType.Companion.promoteToLiteral(entry.getKey(), expectedType, context, substitutions); var actualType = promotedToLiteral != null ? promotedToLiteral : context.getType(entry.getKey()); // Matching with the type of "self" is necessary in particular for choosing the most specific overloads, e.g. // LiteralString-specific methods of str, or for instantiating the type parameters of the containing class // when it's not possible to infer them by other means, e.g. as in the following overload of dict[_KT, _VT].__init__: // def __init__(self: dict[str, _VT], **kwargs: _VT) -> None: ... if (paramWrapper.isSelf()) { // TODO find out a better way to pass the corresponding function inside final PyParameter param = paramWrapper.getParameter(); final PyFunction function = as(ScopeUtil.getScopeOwner(param), PyFunction.class); assert function != null; if (function.getModifier() == PyFunction.Modifier.CLASSMETHOD) { actualType = PyTypeUtil.toStream(actualType) .select(PyClassLikeType.class) .map(PyClassLikeType::toClass) .select(PyType.class) .foldLeft(PyUnionType::union) .orElse(actualType); } else if (PyUtil.isInitMethod(function)) { actualType = PyTypeUtil.toStream(actualType) .select(PyInstantiableType.class) .map(PyInstantiableType::toInstance) .select(PyType.class) .foldLeft(PyUnionType::union) .orElse(actualType); } PyClass containingClass = function.getContainingClass(); assert containingClass != null; PyCollectionType genericClass = findGenericDefinitionType(containingClass, context); if (genericClass != null && !match(genericClass, expectedType, context, substitutions)) { return null; } } if (!match(expectedType, actualType, context, substitutions)) { return null; } } if (!matchContainer(getMappedPositionalContainer(arguments), getArgumentsMappedToPositionalContainer(arguments), substitutions, context)) { return null; } if (!matchContainer(getMappedKeywordContainer(arguments), getArgumentsMappedToKeywordContainer(arguments), substitutions, context)) { return null; } return substitutions; }