PsiType (PsiTypeParameter typeParam, PsiType param, PsiType arg, boolean isContraVariantPosition, LanguageLevel languageLevel) { if (PsiTypes.voidType().equals(arg) || PsiTypes.voidType().equals(param)) return PsiTypes.nullType(); if (param instanceof PsiArrayType && arg instanceof PsiArrayType) { return getSubstitutionForTypeParameter(typeParam, ((PsiArrayType)param).getComponentType(), ((PsiArrayType)arg).getComponentType(), isContraVariantPosition, languageLevel); } if (!(param instanceof PsiClassType)) return PsiTypes.nullType(); if (arg == null) { return PsiTypes.nullType(); } PsiClass paramClass = ((PsiClassType)param).resolve(); if (!(paramClass instanceof PsiTypeParameter) && !arg.isAssignableFrom(((PsiClassType)param).rawType())) { return PsiTypes.nullType(); } final PsiType[] leftTypes; final PsiType[] rightTypes; if (isContraVariantPosition) { leftTypes = new PsiType[] {param}; rightTypes = new PsiType[]{arg}; } else { leftTypes = new PsiType[] {arg}; rightTypes = new PsiType[]{param}; } final PsiTypeParameterListOwner owner = typeParam.getOwner(); final PsiTypeParameter[] typeParams = owner != null ? owner.getTypeParameters() : new PsiTypeParameter[] {typeParam}; final InferenceSession inferenceSession = new InferenceSession(typeParams, leftTypes, rightTypes, PsiSubstitutor.EMPTY, myManager, null); if (inferenceSession.isProperType(inferenceSession.substituteWithInferenceVariables(param)) && inferenceSession.isProperType(inferenceSession.substituteWithInferenceVariables(arg))) { boolean proceed = false; for (PsiClassType classType : typeParam.getExtendsListTypes()) { if (!inferenceSession.isProperType(inferenceSession.substituteWithInferenceVariables(classType))) { proceed = true; break; } } if (!proceed) { return PsiTypes.nullType(); } } final PsiSubstitutor substitutor = inferenceSession.infer(); return substitutor.substitute(typeParam); }