PsiSubstitutor (PsiTypeParameter @NotNull [] typeParameters, PsiType @NotNull [] leftTypes, PsiType @NotNull [] rightTypes, @NotNull PsiSubstitutor partialSubstitutor, @NotNull LanguageLevel languageLevel) { if (leftTypes.length != rightTypes.length) throw new IllegalArgumentException("Types must be of the same length"); PsiSubstitutor substitutor = PsiSubstitutor.EMPTY; for (PsiTypeParameter typeParameter : typeParameters) { PsiType substitution = PsiTypes.nullType(); PsiType lowerBound = PsiTypes.nullType(); for (int i1 = 0; i1 < leftTypes.length; i1++) { PsiType leftType = leftTypes[i1]; PsiType rightType = rightTypes[i1]; final Pair<PsiType, ConstraintType> constraint = getSubstitutionForTypeParameterConstraint(typeParameter, leftType, rightType, true, languageLevel); if (constraint != null) { final ConstraintType constraintType = constraint.getSecond(); final PsiType current = constraint.getFirst(); if (constraintType == ConstraintType.EQUALS) { substitution = current; break; } else if (constraintType == ConstraintType.SUBTYPE) { if (PsiTypes.nullType().equals(substitution)) { substitution = current; } else { substitution = GenericsUtil.getLeastUpperBound(substitution, current, myManager); } } else { if (PsiTypes.nullType().equals(lowerBound)) { lowerBound = current; } else { lowerBound = GenericsUtil.getLeastUpperBound(lowerBound, current, myManager); } } } } if (PsiTypes.nullType().equals(substitution)) { substitution = lowerBound; } if (substitution != PsiTypes.nullType()) { substitutor = substitutor.put(typeParameter, substitution); } } for (int i = 0; i < typeParameters.length; i++) { PsiTypeParameter typeParameter = typeParameters[i]; if (!substitutor.getSubstitutionMap().containsKey(typeParameter)) { PsiType substitutionFromBounds = PsiTypes.nullType(); OtherParameters: for (int j = 0; j < typeParameters.length; j++) { if (i != j) { PsiTypeParameter other = typeParameters[j]; final PsiType otherSubstitution = substitutor.substitute(other); if (otherSubstitution == null) continue; final PsiClassType[] bounds = other.getExtendsListTypes(); for (PsiClassType bound : bounds) { final PsiType substitutedBound = substitutor.substitute(bound); final Pair<PsiType, ConstraintType> currentConstraint = getSubstitutionForTypeParameterConstraint(typeParameter, substitutedBound, otherSubstitution, true, languageLevel); if (currentConstraint == null) continue; final PsiType currentSubstitution = currentConstraint.getFirst(); final ConstraintType currentConstraintType = currentConstraint.getSecond(); if (currentConstraintType == ConstraintType.EQUALS) { substitutionFromBounds = currentSubstitution; break OtherParameters; } else if (currentConstraintType == ConstraintType.SUPERTYPE) { if (PsiTypes.nullType().equals(substitutionFromBounds)) { substitutionFromBounds = currentSubstitution; } else { substitutionFromBounds = GenericsUtil.getLeastUpperBound(substitutionFromBounds, currentSubstitution, myManager); } } } } } if (substitutionFromBounds != PsiTypes.nullType()) { substitutor = substitutor.put(typeParameter, substitutionFromBounds); } } } return substitutor; }