private static @NotNull List<String> collectParameters(@NotNull PyChangeInfo changeInfo, @NotNull PyCallExpression call) {
    boolean keywordArgsRequired = false;
    final List<String> newArguments = new ArrayList<>();

    final TypeEvalContext typeEvalContext = TypeEvalContext.userInitiated(call.getProject(), null);
    final PyFunction function = changeInfo.getMethod();

    final List<PyCallableParameter> allOrigParams = function.getParameters(typeEvalContext);
    final PyCallExpression.PyArgumentsMapping mapping = PyCallExpressionHelper.mapArguments(call, function, typeEvalContext);

    MultiMap<Integer, PyExpression> oldParamIndexToArgs = MultiMap.create();
    for (Map.Entry<PyExpression, PyCallableParameter> entry : mapping.getMappedParameters().entrySet()) {
      final PyCallableParameter param = entry.getValue();
      oldParamIndexToArgs.putValue(allOrigParams.indexOf(param), entry.getKey());
    }
    assert oldParamIndexToArgs.keySet().stream().allMatch(index -> index >= 0);

    List<PyParameterInfo> newParamInfos = Arrays.asList(changeInfo.getNewParameters());
    
    final int posVarargIndex = ContainerUtil.indexOf(newParamInfos, info -> isPositionalVarargName(info.getName()));
    final int posOnlyMarkerIndex = ContainerUtil.indexOf(newParamInfos, info -> PySlashParameter.TEXT.equals(info.getName()));
    final boolean posVarargEmpty = posVarargIndex != -1 && oldParamIndexToArgs.get(newParamInfos.get(posVarargIndex).getOldIndex()).isEmpty();
    List<PyExpression> notInsertedVariadicKeywordArgs =
      new ArrayList<>(ContainerUtil.filter(call.getArguments(), a -> a instanceof PyStarArgument && ((PyStarArgument)a).isKeyword()));
    boolean variadicKeywordArgsUsed = false;
    final int implicitCount = mapping.getImplicitParameters().size();
    for (int paramIndex = implicitCount; paramIndex < newParamInfos.size(); paramIndex++) {
      PyParameterInfo info = newParamInfos.get(paramIndex);
      final String paramName = info.getName();
      final boolean isKeywordVararg = isKeywordVarargName(paramName);
      final boolean isPositionalVararg = isPositionalVarargName(paramName);
      final boolean beforePositionalOnlyMarker = paramIndex < posOnlyMarkerIndex;
      final boolean defaultShouldBeInlined = beforePositionalOnlyMarker &&
                                             ContainerUtil.exists(newParamInfos.subList(paramIndex + 1, posOnlyMarkerIndex),
                                                                  i -> !i.isNew() && !oldParamIndexToArgs.get(i.getOldIndex()).isEmpty());
      if (paramName.equals(PySingleStarParameter.TEXT)) {
        keywordArgsRequired = true;
        continue;
      }
      if (paramName.equals(PySlashParameter.TEXT)) {
        continue;
      }
      final String paramDefault = StringUtil.notNullize(info.getDefaultValue());
      final int oldIndex = info.getOldIndex();
      if (oldIndex < 0) {
        if (info.getDefaultInSignature() && !defaultShouldBeInlined) {
          // If the next argument was passed by position it would match with this new default.
          // Imagine "def f(x, y=None): ..." -> "def f(x, foo=None, y=None): ..." and a call "f(1, 2)"
          keywordArgsRequired = true;
        }
        else if (!isKeywordVararg && !isPositionalVararg) {
          newArguments.add(formatArgument(paramName, paramDefault, keywordArgsRequired));
        }
      }
      else {
        final Collection<PyExpression> existingArgs = oldParamIndexToArgs.get(oldIndex);
        final PyCallableParameter oldParam = allOrigParams.get(oldIndex);
        final boolean usesValueFromVariadic = mapping.getParametersMappedToVariadicKeywordArguments().contains(oldParam);
        variadicKeywordArgsUsed |= usesValueFromVariadic;
        if (!existingArgs.isEmpty()) {
          for (PyExpression arg : existingArgs) {
            PyExpression argValue;
            String argName;
            if (arg instanceof PyKeywordArgument) {
              argValue = ((PyKeywordArgument)arg).getValueExpression();
              argName = StringUtil.notNullize(((PyKeywordArgument)arg).getKeyword());
            }
            else {
              argValue = arg;
              argName = "";
            }
            notInsertedVariadicKeywordArgs.remove(argValue);
            // Keep format of existing keyword arguments unless it's illegal in their new position
            if (!argName.isEmpty() && !(paramIndex < posVarargIndex && !posVarargEmpty) && !beforePositionalOnlyMarker) {
              keywordArgsRequired = true;
            }
            assert !(isPositionalVararg && keywordArgsRequired);
            final String argValueText = argValue != null ? argValue.getText() : "";
            final String newArgumentName = isKeywordVararg ? argName : paramName;
            newArguments.add(formatArgument(newArgumentName, argValueText, keywordArgsRequired));
          }
        }
        // Parameter receives its default value from the signature, all subsequent arguments must use keyword form
        else if (info.getDefaultInSignature() && !defaultShouldBeInlined) {
          keywordArgsRequired = true;
        }
        else if (!isPositionalVararg && !isKeywordVararg && !usesValueFromVariadic) {
          // Existing ordinary parameter with neither a default value in the signature, not a corresponding argument.
          // Most likely its default was propagated from the signature down to calls.
          newArguments.add(formatArgument(paramName, paramDefault, keywordArgsRequired));
        }
      }
      // Keyword-only arguments should follow (Python 3) 
      if (isPositionalVararg) {
        keywordArgsRequired = true;
      }
    }
    if (variadicKeywordArgsUsed) {
      newArguments.addAll(ContainerUtil.map(notInsertedVariadicKeywordArgs, PsiElement::getText));
    }

    return newArguments;
  }
private static @NotNull List<String> collectParameters(@NotNull PyChangeInfo changeInfo, @NotNull PyCallExpression call)