@NotNull private static Specifics isFunctionalTypeMoreSpecific( @NotNull CandidateInfo method, @NotNull CandidateInfo conflict, PsiExpression expr, int functionalInterfaceIdx) { if (expr instanceof PsiParenthesizedExpression) { return isFunctionalTypeMoreSpecific( method, conflict, ((PsiParenthesizedExpression) expr).getExpression(), functionalInterfaceIdx); } if (expr instanceof PsiConditionalExpression) { final Specifics thenSpecifics = isFunctionalTypeMoreSpecific( method, conflict, ((PsiConditionalExpression) expr).getThenExpression(), functionalInterfaceIdx); final Specifics elseSpecifics = isFunctionalTypeMoreSpecific( method, conflict, ((PsiConditionalExpression) expr).getElseExpression(), functionalInterfaceIdx); return thenSpecifics == elseSpecifics ? thenSpecifics : Specifics.NEITHER; } if (expr instanceof PsiFunctionalExpression) { if (expr instanceof PsiLambdaExpression && !((PsiLambdaExpression) expr).hasFormalParameterTypes()) { return Specifics.NEITHER; } if (expr instanceof PsiMethodReferenceExpression && !((PsiMethodReferenceExpression) expr).isExact()) { return Specifics.NEITHER; } final PsiType sType = getFunctionalType(functionalInterfaceIdx, method); final PsiType tType = getFunctionalType(functionalInterfaceIdx, conflict); if (LambdaUtil.isFunctionalType(sType) && LambdaUtil.isFunctionalType(tType)) { final boolean specific12 = InferenceSession.isFunctionalTypeMoreSpecificOnExpression(sType, tType, expr); final boolean specific21 = InferenceSession.isFunctionalTypeMoreSpecificOnExpression(tType, sType, expr); if (specific12 && !specific21) return Specifics.FIRST; if (!specific12 && specific21) return Specifics.SECOND; } } return Specifics.NEITHER; }
private Specifics isMoreSpecific( @NotNull MethodCandidateInfo info1, @NotNull MethodCandidateInfo info2, @MethodCandidateInfo.ApplicabilityLevelConstant int applicabilityLevel, @NotNull LanguageLevel languageLevel) { PsiMethod method1 = info1.getElement(); PsiMethod method2 = info2.getElement(); final PsiClass class1 = method1.getContainingClass(); final PsiClass class2 = method2.getContainingClass(); final PsiParameter[] params1 = method1.getParameterList().getParameters(); final PsiParameter[] params2 = method2.getParameterList().getParameters(); final PsiTypeParameter[] typeParameters1 = method1.getTypeParameters(); final PsiTypeParameter[] typeParameters2 = method2.getTypeParameters(); final PsiSubstitutor classSubstitutor1 = info1.getSubstitutor(false); // substitutions for method type parameters will be ignored final PsiSubstitutor classSubstitutor2 = info2.getSubstitutor(false); final int max = Math.max(params1.length, params2.length); PsiType[] types1 = PsiType.createArray(max); PsiType[] types2 = PsiType.createArray(max); final boolean varargsPosition = applicabilityLevel == MethodCandidateInfo.ApplicabilityLevel.VARARGS; for (int i = 0; i < max; i++) { ProgressManager.checkCanceled(); PsiType type1 = params1.length > 0 ? params1[Math.min(i, params1.length - 1)].getType() : null; PsiType type2 = params2.length > 0 ? params2[Math.min(i, params2.length - 1)].getType() : null; if (varargsPosition) { if (type1 instanceof PsiEllipsisType && type2 instanceof PsiEllipsisType && params1.length == params2.length && class1 != null && (!JavaVersionService.getInstance().isAtLeast(class1, JavaSdkVersion.JDK_1_7) || ((PsiArrayType) type1) .getComponentType() .equalsToText(CommonClassNames.JAVA_LANG_OBJECT) || ((PsiArrayType) type2) .getComponentType() .equalsToText(CommonClassNames.JAVA_LANG_OBJECT))) { type1 = ((PsiEllipsisType) type1).toArrayType(); type2 = ((PsiEllipsisType) type2).toArrayType(); } else { type1 = type1 instanceof PsiEllipsisType ? ((PsiArrayType) type1).getComponentType() : type1; type2 = type2 instanceof PsiEllipsisType ? ((PsiArrayType) type2).getComponentType() : type2; } } types1[i] = type1; types2[i] = type2; } boolean sameBoxing = true; int[] boxingHappened = new int[2]; for (int i = 0; i < types1.length; i++) { ProgressManager.checkCanceled(); PsiType type1 = classSubstitutor1.substitute(types1[i]); PsiType type2 = classSubstitutor2.substitute(types2[i]); PsiType argType = i < getActualParameterTypes().length ? getActualParameterTypes()[i] : null; boolean boxingInFirst = false; if (isBoxingHappened(argType, type1, languageLevel)) { boxingHappened[0] += 1; boxingInFirst = true; } boolean boxingInSecond = false; if (isBoxingHappened(argType, type2, languageLevel)) { boxingHappened[1] += 1; boxingInSecond = true; } sameBoxing &= boxingInFirst == boxingInSecond; } if (boxingHappened[0] == 0 && boxingHappened[1] > 0) return Specifics.FIRST; if (boxingHappened[0] > 0 && boxingHappened[1] == 0) return Specifics.SECOND; if (sameBoxing) { final PsiSubstitutor siteSubstitutor1 = info1.getSiteSubstitutor(); final PsiSubstitutor siteSubstitutor2 = info2.getSiteSubstitutor(); final PsiType[] types2AtSite = typesAtSite(types2, siteSubstitutor2); final PsiType[] types1AtSite = typesAtSite(types1, siteSubstitutor1); final PsiSubstitutor methodSubstitutor1 = calculateMethodSubstitutor( typeParameters1, method1, siteSubstitutor1, types1, types2AtSite, languageLevel); boolean applicable12 = isApplicableTo( types2AtSite, method1, languageLevel, varargsPosition, methodSubstitutor1, method2); final PsiSubstitutor methodSubstitutor2 = calculateMethodSubstitutor( typeParameters2, method2, siteSubstitutor2, types2, types1AtSite, languageLevel); boolean applicable21 = isApplicableTo( types1AtSite, method2, languageLevel, varargsPosition, methodSubstitutor2, method1); if (!myLanguageLevel.isAtLeast(LanguageLevel.JDK_1_8)) { final boolean typeArgsApplicable12 = GenericsUtil.isTypeArgumentsApplicable( typeParameters1, methodSubstitutor1, myArgumentsList, !applicable21); final boolean typeArgsApplicable21 = GenericsUtil.isTypeArgumentsApplicable( typeParameters2, methodSubstitutor2, myArgumentsList, !applicable12); if (!typeArgsApplicable12) { applicable12 = false; } if (!typeArgsApplicable21) { applicable21 = false; } } if (applicable12 || applicable21) { if (applicable12 && !applicable21) return Specifics.SECOND; if (applicable21 && !applicable12) return Specifics.FIRST; final boolean abstract1 = method1.hasModifierProperty(PsiModifier.ABSTRACT); final boolean abstract2 = method2.hasModifierProperty(PsiModifier.ABSTRACT); if (abstract1 && !abstract2) { return Specifics.SECOND; } if (abstract2 && !abstract1) { return Specifics.FIRST; } } if (languageLevel.isAtLeast(LanguageLevel.JDK_1_8) && myArgumentsList instanceof PsiExpressionList && (typeParameters1.length == 0 || typeParameters2.length == 0)) { boolean toCompareFunctional = false; if (types1.length > 0 && types2.length > 0) { for (int i = 0; i < getActualParametersLength(); i++) { final PsiType type1 = types1[Math.min(i, types1.length - 1)]; final PsiType type2 = types2[Math.min(i, types2.length - 1)]; // from 15.12.2.5 Choosing the Most Specific Method // In addition, a functional interface type S is more specific than a functional // interface type T for an expression exp // if T is not a subtype of S and one of the following conditions apply. if (LambdaUtil.isFunctionalType(type1) && !TypeConversionUtil.erasure(type1).isAssignableFrom(type2) && LambdaUtil.isFunctionalType(type2) && !TypeConversionUtil.erasure(type2).isAssignableFrom(type1)) { types1AtSite[Math.min(i, types1.length - 1)] = PsiType.NULL; types2AtSite[Math.min(i, types2.length - 1)] = PsiType.NULL; toCompareFunctional = true; } } } if (toCompareFunctional) { final boolean applicable12ignoreFunctionalType = isApplicableTo( types2AtSite, method1, languageLevel, varargsPosition, calculateMethodSubstitutor( typeParameters1, method1, siteSubstitutor1, types1, types2AtSite, languageLevel), null); final boolean applicable21ignoreFunctionalType = isApplicableTo( types1AtSite, method2, languageLevel, varargsPosition, calculateMethodSubstitutor( typeParameters2, method2, siteSubstitutor2, types2, types1AtSite, languageLevel), null); if (applicable12ignoreFunctionalType || applicable21ignoreFunctionalType) { Specifics specifics = null; for (int i = 0; i < getActualParametersLength(); i++) { if (types1AtSite[Math.min(i, types1.length - 1)] == PsiType.NULL && types2AtSite[Math.min(i, types2.length - 1)] == PsiType.NULL) { Specifics specific = isFunctionalTypeMoreSpecific( info1, info2, ((PsiExpressionList) myArgumentsList).getExpressions()[i], i); if (specific == Specifics.NEITHER) { specifics = Specifics.NEITHER; break; } if (specifics == null) { specifics = specific; } else if (specifics != specific) { specifics = Specifics.NEITHER; break; } } } if (!applicable12ignoreFunctionalType && applicable21ignoreFunctionalType) { return specifics == Specifics.FIRST ? Specifics.FIRST : Specifics.NEITHER; } if (!applicable21ignoreFunctionalType && applicable12ignoreFunctionalType) { return specifics == Specifics.SECOND ? Specifics.SECOND : Specifics.NEITHER; } return specifics; } } } } else if (varargsPosition) { final PsiType lastParamType1 = classSubstitutor1.substitute(types1[types1.length - 1]); final PsiType lastParamType2 = classSubstitutor2.substitute(types2[types1.length - 1]); final boolean assignable1 = TypeConversionUtil.isAssignable(lastParamType2, lastParamType1); final boolean assignable2 = TypeConversionUtil.isAssignable(lastParamType1, lastParamType2); if (assignable1 && !assignable2) { return Specifics.FIRST; } if (assignable2 && !assignable1) { return Specifics.SECOND; } } if (class1 != class2) { if (class2.isInheritor(class1, true) || class1.isInterface() && !class2.isInterface()) { if (MethodSignatureUtil.isSubsignature( method1.getSignature(info1.getSubstitutor(false)), method2.getSignature(info2.getSubstitutor(false)))) { return Specifics.SECOND; } else if (method1.hasModifierProperty(PsiModifier.STATIC) && method2.hasModifierProperty(PsiModifier.STATIC) && boxingHappened[0] == 0) { return Specifics.SECOND; } } else if (MethodSignatureUtil.areErasedParametersEqual( method1.getSignature(PsiSubstitutor.EMPTY), method2.getSignature(PsiSubstitutor.EMPTY)) && MethodSignatureUtil.isSubsignature( method2.getSignature(info2.getSubstitutor(false)), method1.getSignature(info1.getSubstitutor(false)))) { return Specifics.FIRST; } else if (class1.isInheritor(class2, true) || class2.isInterface()) { if (method1.hasModifierProperty(PsiModifier.STATIC) && method2.hasModifierProperty(PsiModifier.STATIC) && boxingHappened[0] == 0) { return Specifics.FIRST; } } } final boolean raw1 = PsiUtil.isRawSubstitutor(method1, classSubstitutor1); final boolean raw2 = PsiUtil.isRawSubstitutor(method2, classSubstitutor2); if (raw1 ^ raw2) { return raw1 ? Specifics.SECOND : Specifics.FIRST; } final boolean varargs1 = info1.isVarargs(); final boolean varargs2 = info2.isVarargs(); if (varargs1 ^ varargs2) { return varargs1 ? Specifics.SECOND : Specifics.FIRST; } return Specifics.NEITHER; }