private List<PsiType> matchingTypeParameters(
      PsiType[] paramVals, PsiTypeParameter[] params, ExpectedTypeInfo info) {
    PsiType type = info.getType();
    int kind = info.getKind();

    List<PsiType> result = new ArrayList<>();
    for (int i = 0; i < paramVals.length; i++) {
      PsiType val = paramVals[i];
      if (val != null) {
        switch (kind) {
          case ExpectedTypeInfo.TYPE_STRICTLY:
            if (val.equals(type)) result.add(myFactory.createType(params[i]));
            break;
          case ExpectedTypeInfo.TYPE_OR_SUBTYPE:
            if (type.isAssignableFrom(val)) result.add(myFactory.createType(params[i]));
            break;
          case ExpectedTypeInfo.TYPE_OR_SUPERTYPE:
            if (val.isAssignableFrom(type)) result.add(myFactory.createType(params[i]));
            break;
        }
      }
    }

    return result;
  }
  private int substituteToTypeParameters(
      PsiTypeElement typeElement,
      PsiTypeElement inplaceTypeElement,
      PsiType[] paramVals,
      PsiTypeParameter[] params,
      TemplateBuilder builder,
      PsiSubstitutor rawingSubstitutor,
      boolean toplevel) {
    PsiType type = inplaceTypeElement.getType();
    List<PsiType> types = new ArrayList<>();
    for (int i = 0; i < paramVals.length; i++) {
      PsiType val = paramVals[i];
      if (val == null) return SUBSTITUTED_NONE;
      if (type.equals(val)) {
        types.add(myFactory.createType(params[i]));
      }
    }

    if (!types.isEmpty()) {
      Project project = typeElement.getProject();
      PsiType substituted = rawingSubstitutor.substitute(type);
      if (!CommonClassNames.JAVA_LANG_OBJECT.equals(substituted.getCanonicalText())
          && (toplevel || substituted.equals(type))) {
        types.add(substituted);
      }

      builder.replaceElement(
          typeElement,
          new TypeExpression(project, types.toArray(PsiType.createArray(types.size()))));
      return toplevel ? SUBSTITUTED_IN_REF : SUBSTITUTED_IN_PARAMETERS;
    }

    boolean substituted = false;
    PsiJavaCodeReferenceElement ref = typeElement.getInnermostComponentReferenceElement();
    PsiJavaCodeReferenceElement inplaceRef =
        inplaceTypeElement.getInnermostComponentReferenceElement();
    if (ref != null) {
      LOG.assertTrue(inplaceRef != null);
      PsiTypeElement[] innerTypeElements = ref.getParameterList().getTypeParameterElements();
      PsiTypeElement[] inplaceInnerTypeElements =
          inplaceRef.getParameterList().getTypeParameterElements();
      for (int i = 0; i < innerTypeElements.length; i++) {
        substituted |=
            substituteToTypeParameters(
                    innerTypeElements[i],
                    inplaceInnerTypeElements[i],
                    paramVals,
                    params,
                    builder,
                    rawingSubstitutor,
                    false)
                != SUBSTITUTED_NONE;
      }
    }

    return substituted ? SUBSTITUTED_IN_PARAMETERS : SUBSTITUTED_NONE;
  }