@NotNull
  private static Couple<List<String>> buildNewFunctionParamsAndSuperInitCallArgs(@NotNull ParametersInfo origInfo,
                                                                                 @NotNull ParametersInfo superInfo,
                                                                                 boolean addSelfToCall) {
    final List<String> newFunctionParams = new ArrayList<>();
    final List<String> superCallArgs = new ArrayList<>();

    final PyParameter selfParameter = origInfo.getSelfParameter();
    if (selfParameter != null && StringUtil.isNotEmpty(selfParameter.getName())) {
      newFunctionParams.add(selfParameter.getText());
    }
    else {
      newFunctionParams.add(PyNames.CANONICAL_SELF);
    }

    if (addSelfToCall) {
      superCallArgs.add(getSelfParameterName(origInfo));
    }

    // Required parameters (not-keyword)
    for (PyParameter param : origInfo.getRequiredParameters()) {
      newFunctionParams.add(param.getText());
    }
    for (PyParameter param : superInfo.getRequiredParameters()) {
      // Special case as if base class has constructor __init__((a, b), c) and
      // subclass has constructor __init__(a, (b, c))
      final PyTupleParameter tupleParam = param.getAsTuple();
      if (tupleParam != null) {
        final List<String> uniqueNames = collectParameterNames(tupleParam);
        final boolean hasDuplicates = uniqueNames.removeAll(origInfo.getAllParameterNames());
        if (hasDuplicates) {
          newFunctionParams.addAll(uniqueNames);
        }
        else {
          newFunctionParams.add(param.getText());
        }
        // Retain original structure of tuple parameter.
        // Note that tuple parameters cannot have annotations or nested default values, so it's syntactically safe
        superCallArgs.add(param.getText());
      }
      else {
        if (!origInfo.getAllParameterNames().contains(param.getName())) {
          newFunctionParams.add(param.getText());
        }
        superCallArgs.add(param.getName());
      }
    }

    // Optional parameters (not-keyword)
    for (PyParameter param : origInfo.getOptionalParameters()) {
      newFunctionParams.add(param.getText());
    }

    // Pass parameters with default values to super class constructor, only if both functions contain them
    for (PyParameter param : superInfo.getOptionalParameters()) {
      final PyTupleParameter tupleParam = param.getAsTuple();
      if (tupleParam != null) {
        if (origInfo.getAllParameterNames().containsAll(collectParameterNames(tupleParam))) {
          final String paramText = tupleParam.getText();
          final PsiElement equalSign = PyPsiUtils.getFirstChildOfType(param, PyTokenTypes.EQ);
          if (equalSign != null) {
            superCallArgs.add(paramText.substring(0, equalSign.getStartOffsetInParent()).trim());
          }
        }
      }
      else {
        if (origInfo.getAllParameterNames().contains(param.getName())) {
          superCallArgs.add(param.getName());
        }
      }
    }

    // Positional vararg
    PyParameter starredParam = null;
    if (origInfo.getPositionalContainerParameter() != null) {
      starredParam = origInfo.getPositionalContainerParameter();
    }
    else if (superInfo.getPositionalContainerParameter() != null) {
      starredParam = superInfo.getPositionalContainerParameter();
    }
    else if (origInfo.getSingleStarParameter() != null) {
      starredParam = origInfo.getSingleStarParameter();
    }
    else if (superInfo.getSingleStarParameter() != null) {
      starredParam = superInfo.getSingleStarParameter();
    }
    if (starredParam != null) {
      newFunctionParams.add(starredParam.getText());
      if (superInfo.getPositionalContainerParameter() != null) {
        superCallArgs.add("*" + starredParam.getName());
      }
    }

    // Required keyword-only parameters
    boolean newSignatureContainsKeywordParams = false;
    for (PyParameter param : origInfo.getRequiredKeywordOnlyParameters()) {
      newFunctionParams.add(param.getText());
      newSignatureContainsKeywordParams = true;
    }
    for (PyParameter param : superInfo.getRequiredKeywordOnlyParameters()) {
      if (!origInfo.getAllParameterNames().contains(param.getName())) {
        newFunctionParams.add(param.getText());
        newSignatureContainsKeywordParams = true;
      }
      superCallArgs.add(param.getName() + "=" + param.getName());
    }

    // Optional keyword-only parameters
    for (PyParameter param : origInfo.getOptionalKeywordOnlyParameters()) {
      newFunctionParams.add(param.getText());
      newSignatureContainsKeywordParams = true;
    }

    // If '*' param is followed by nothing in result signature, remove it altogether
    if (starredParam instanceof PySingleStarParameter && !newSignatureContainsKeywordParams) {
      newFunctionParams.remove(newFunctionParams.size() - 1);
    }

    for (PyParameter param : superInfo.getOptionalKeywordOnlyParameters()) {
      if (origInfo.getAllParameterNames().contains(param.getName())) {
        superCallArgs.add(param.getName() + "=" + param.getName());
      }
    }

    // Keyword vararg
    PyParameter doubleStarredParam = null;
    if (origInfo.getKeywordContainerParameter() != null) {
      doubleStarredParam = origInfo.getKeywordContainerParameter();
    }
    else if (superInfo.getKeywordContainerParameter() != null) {
      doubleStarredParam = superInfo.getKeywordContainerParameter();
    }
    if (doubleStarredParam != null) {
      newFunctionParams.add(doubleStarredParam.getText());
      if (superInfo.getKeywordContainerParameter() != null) {
        superCallArgs.add("**" + doubleStarredParam.getName());
      }
    }
    return Couple.of(newFunctionParams, superCallArgs);
  }
@NotNull
  private static Couple<List<String>> buildNewFunctionParamsAndSuperInitCallArgs(@NotNull ParametersInfo origInfo,
                                                                                 @NotNull ParametersInfo superInfo,
                                                                                 boolean addSelfToCall)