public static @Nullable PyTypeParameterMapping mapWithParameterList(@NotNull List<? extends PyType> expectedParameterTypes,
                                                                      @NotNull List<PyCallableParameter> actualParameters,
                                                                      @NotNull TypeEvalContext context) {
    List<PyType> flattenedExpectedParameterTypes = flattenUnpackedTupleTypes(expectedParameterTypes);
    int expectedArity = ContainerUtil.exists(flattenedExpectedParameterTypes, Conditions.instanceOf(PyVariadicType.class))
                        ? -1
                        : flattenedExpectedParameterTypes.size();

    List<PyType> requiredPositionalArgumentTypes = new ArrayList<>();
    List<PyType> optionalPositionalArgumentTypes = new ArrayList<>();
    List<PyType> positionalVarargArgumentTypes = new SmartList<>();

    for (PyCallableParameter parameter : actualParameters) {
      if (parameter.isSelf()
          || parameter.getParameter() instanceof PySlashParameter
          || parameter.getParameter() instanceof PySingleStarParameter
          || parameter.isKeywordContainer()) {
        continue;
      }
      if (parameter.getParameter() instanceof PyNamedParameter namedParameter && namedParameter.isKeywordOnly()) {
        if (!namedParameter.hasDefaultValue()) {
          return null;
        }
        continue;
      }
      PyType actualParameterType = parameter.getType(context);
      if (parameter.isPositionalContainer()) {
        // Convert an automatic tuple[MyType, ...] to *tuple[MyType, ...] for matching purposes
        if (actualParameterType instanceof PyTupleType argsTupleType) {
          positionalVarargArgumentTypes.addAll(flattenUnpackedTupleTypes(Collections.singletonList(argsTupleType.asUnpackedTupleType())));
        }
        else {
          positionalVarargArgumentTypes.addAll(flattenUnpackedTupleTypes(Collections.singletonList(actualParameterType)));
        }
      }
      else if (parameter.hasDefaultValue()) {
        optionalPositionalArgumentTypes.add(actualParameterType);
      }
      else {
        requiredPositionalArgumentTypes.addAll(flattenUnpackedTupleTypes(Collections.singletonList(actualParameterType)));
      }
    }

    if (positionalVarargArgumentTypes.size() > 1 ||
        positionalVarargArgumentTypes.size() == 1 && !(positionalVarargArgumentTypes.get(0) instanceof PyVariadicType)) {
      requiredPositionalArgumentTypes.addAll(optionalPositionalArgumentTypes);
      optionalPositionalArgumentTypes.clear();
      requiredPositionalArgumentTypes.addAll(positionalVarargArgumentTypes);
      positionalVarargArgumentTypes.clear();
    }

    int actualArity = ContainerUtil.exists(requiredPositionalArgumentTypes, Conditions.instanceOf(PyVariadicType.class)) ?
                      -1 :
                      requiredPositionalArgumentTypes.size();


    if (expectedArity != -1 && actualArity != -1) {
      if (actualArity > expectedArity) {
        return null;
      }
      List<PyType> arityAdjustedActualParameterTypes = new ArrayList<>(requiredPositionalArgumentTypes);
      arityAdjustedActualParameterTypes.addAll(optionalPositionalArgumentTypes.subList(
        0, Math.min(optionalPositionalArgumentTypes.size(), expectedArity - arityAdjustedActualParameterTypes.size())
      ));
      if (!positionalVarargArgumentTypes.isEmpty() && expectedArity - arityAdjustedActualParameterTypes.size() > 0) {
        assert positionalVarargArgumentTypes.size() == 1 && positionalVarargArgumentTypes.get(0) instanceof PyVariadicType;
        arityAdjustedActualParameterTypes.add(positionalVarargArgumentTypes.get(0));
      }
      return mapByShape(flattenedExpectedParameterTypes, arityAdjustedActualParameterTypes);
    }
    return mapByShape(flattenedExpectedParameterTypes,
                      ContainerUtil.concat(requiredPositionalArgumentTypes,
                                           optionalPositionalArgumentTypes,
                                           positionalVarargArgumentTypes));
  }
public static @Nullable PyTypeParameterMapping mapWithParameterList(@NotNull List<? extends PyType> expectedParameterTypes,
                                                                      @NotNull List<PyCallableParameter> actualParameters,
                                                                      @NotNull TypeEvalContext context)