PsiSubstitutor (PsiTypeParameter @NotNull [] typeParameters, PsiParameter @NotNull [] parameters, PsiExpression @NotNull [] arguments, @Nullable MethodCandidateInfo currentMethod, @NotNull PsiSubstitutor partialSubstitutor, @NotNull PsiElement parent, @NotNull ParameterTypeInferencePolicy policy, @NotNull LanguageLevel languageLevel) { PsiType[] substitutions = PsiType.createArray(typeParameters.length); @SuppressWarnings("unchecked") Pair<PsiType, ConstraintType>[] constraints = new Pair[typeParameters.length]; for (int i = 0; i < typeParameters.length; i++) { if (substitutions[i] != null) continue; final Pair<PsiType, ConstraintType> constraint = inferTypeForMethodTypeParameterInner(typeParameters[i], parameters, arguments, partialSubstitutor, null, policy); constraints[i] = constraint; if (constraint != null && constraint.getSecond() != ConstraintType.SUBTYPE) { substitutions[i] = constraint.getFirst(); if (substitutions[i] != null && languageLevel.isAtLeast(LanguageLevel.JDK_1_8)) { //try once more partialSubstitutor = partialSubstitutor.put(typeParameters[i], substitutions[i]); i = -1; } } } for (int i = 0; i < typeParameters.length; i++) { PsiTypeParameter typeParameter = typeParameters[i]; if (substitutions[i] == null) { PsiType substitutionFromBounds = PsiTypes.nullType(); OtherParameters: for (int j = 0; j < typeParameters.length; j++) { if (i != j) { PsiTypeParameter other = typeParameters[j]; final PsiType otherSubstitution = substitutions[j]; if (otherSubstitution == null) continue; final PsiClassType[] bounds = other.getExtendsListTypes(); for (PsiClassType bound : bounds) { final PsiType substitutedBound = partialSubstitutor.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; if (currentSubstitution == null) { constraints[i] = FAILED_INFERENCE; } break OtherParameters; } else if (currentConstraintType == ConstraintType.SUPERTYPE && !JavaVersionService.getInstance().isAtLeast(parent, JavaSdkVersion.JDK_1_7)) { if (PsiTypes.nullType().equals(substitutionFromBounds)) { substitutionFromBounds = currentSubstitution; } else { substitutionFromBounds = GenericsUtil.getLeastUpperBound(substitutionFromBounds, currentSubstitution, myManager); } } } } } if (substitutionFromBounds != PsiTypes.nullType()) substitutions[i] = substitutionFromBounds; } } for (int i = 0; i < typeParameters.length; i++) { PsiTypeParameter typeParameter = typeParameters[i]; PsiType substitution = substitutions[i]; if (substitution != PsiTypes.nullType()) { partialSubstitutor = partialSubstitutor.put(typeParameter, substitution); } } for (int i = 0; i < typeParameters.length; i++) { PsiTypeParameter typeParameter = typeParameters[i]; PsiType substitution = substitutions[i]; if (substitution != null) continue; Pair<PsiType, ConstraintType> constraint = constraints[i]; if (constraint == null) { constraint = inferMethodTypeParameterFromParent(typeParameter, partialSubstitutor, parent, policy); } else if (constraint.getSecond() == ConstraintType.SUBTYPE) { Pair<PsiType, ConstraintType> otherConstraint = inferMethodTypeParameterFromParent(typeParameter, partialSubstitutor, parent, policy); if (otherConstraint != null) { if (otherConstraint.getSecond() == ConstraintType.EQUALS || otherConstraint.getSecond() == ConstraintType.SUPERTYPE || compareSubtypes(constraint.getFirst(), otherConstraint.getFirst())) { constraint = otherConstraint; } } } if (constraint != null) { substitution = constraint.getFirst(); } if (substitution == null) { PsiElementFactory factory = JavaPsiFacade.getElementFactory(myManager.getProject()); return factory.createRawSubstitutor(partialSubstitutor, typeParameters); } if (substitution != PsiTypes.nullType()) { partialSubstitutor = partialSubstitutor.put(typeParameter, substitution); } } return partialSubstitutor; }