mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
most specific check: ensure that functional types are specifically checked when method is not generics (IDEA-136287)
This commit is contained in:
@@ -480,31 +480,55 @@ public final class PsiUtil extends PsiUtilCore {
|
||||
return getApplicabilityLevel(method, substitutorForMethod, args, languageLevel, true, true);
|
||||
}
|
||||
|
||||
|
||||
public interface ApplicabilityChecker {
|
||||
ApplicabilityChecker ASSIGNABILITY_CHECKER = new ApplicabilityChecker() {
|
||||
@Override
|
||||
public boolean isApplicable(PsiType left, PsiType right, boolean allowUncheckedConversion, int argId) {
|
||||
return TypeConversionUtil.isAssignable(left, right, allowUncheckedConversion);
|
||||
}
|
||||
};
|
||||
|
||||
boolean isApplicable(PsiType left, PsiType right, boolean allowUncheckedConversion, int argId);
|
||||
}
|
||||
|
||||
@MethodCandidateInfo.ApplicabilityLevelConstant
|
||||
public static int getApplicabilityLevel(@NotNull final PsiMethod method,
|
||||
@NotNull final PsiSubstitutor substitutorForMethod,
|
||||
@NotNull final PsiType[] args,
|
||||
@NotNull final LanguageLevel languageLevel,
|
||||
final boolean allowUncheckedConversion,
|
||||
final boolean checkVarargs) {
|
||||
return getApplicabilityLevel(method, substitutorForMethod, args, languageLevel,
|
||||
allowUncheckedConversion, checkVarargs, ApplicabilityChecker.ASSIGNABILITY_CHECKER);
|
||||
}
|
||||
|
||||
@MethodCandidateInfo.ApplicabilityLevelConstant
|
||||
public static int getApplicabilityLevel(@NotNull final PsiMethod method,
|
||||
@NotNull final PsiSubstitutor substitutorForMethod,
|
||||
@NotNull final PsiType[] args,
|
||||
@NotNull final LanguageLevel languageLevel,
|
||||
final boolean allowUncheckedConversion,
|
||||
final boolean checkVarargs) {
|
||||
final boolean checkVarargs,
|
||||
@NotNull final ApplicabilityChecker function) {
|
||||
final PsiParameter[] parms = method.getParameterList().getParameters();
|
||||
if (args.length < parms.length - 1) return ApplicabilityLevel.NOT_APPLICABLE;
|
||||
|
||||
final PsiClass containingClass = method.getContainingClass();
|
||||
final boolean isRaw = containingClass != null && isRawSubstitutor(method, substitutorForMethod) && isRawSubstitutor(containingClass, substitutorForMethod);
|
||||
if (!areFirstArgumentsApplicable(args, parms, languageLevel, substitutorForMethod, isRaw)) return ApplicabilityLevel.NOT_APPLICABLE;
|
||||
if (!areFirstArgumentsApplicable(args, parms, languageLevel, substitutorForMethod, isRaw, allowUncheckedConversion, function)) return ApplicabilityLevel.NOT_APPLICABLE;
|
||||
if (args.length == parms.length) {
|
||||
if (parms.length == 0) return ApplicabilityLevel.FIXED_ARITY;
|
||||
PsiType parmType = getParameterType(parms[parms.length - 1], languageLevel, substitutorForMethod);
|
||||
PsiType argType = args[args.length - 1];
|
||||
if (argType == null) return ApplicabilityLevel.NOT_APPLICABLE;
|
||||
if (TypeConversionUtil.isAssignable(parmType, argType, allowUncheckedConversion)) return ApplicabilityLevel.FIXED_ARITY;
|
||||
if (function.isApplicable(parmType, argType, allowUncheckedConversion, parms.length - 1)) return ApplicabilityLevel.FIXED_ARITY;
|
||||
|
||||
if (isRaw) {
|
||||
final PsiType erasedParamType = TypeConversionUtil.erasure(parmType);
|
||||
final PsiType erasedArgType = TypeConversionUtil.erasure(argType);
|
||||
if (erasedArgType != null && erasedParamType != null &&
|
||||
TypeConversionUtil.isAssignable(erasedParamType, erasedArgType)) {
|
||||
function.isApplicable(erasedParamType, erasedArgType, allowUncheckedConversion, parms.length - 1)) {
|
||||
return ApplicabilityLevel.FIXED_ARITY;
|
||||
}
|
||||
}
|
||||
@@ -523,7 +547,7 @@ public final class PsiUtil extends PsiUtilCore {
|
||||
}
|
||||
for (int i = parms.length - 1; i < args.length; i++) {
|
||||
PsiType argType = args[i];
|
||||
if (argType == null || !TypeConversionUtil.isAssignable(lastParmType, argType)) {
|
||||
if (argType == null || !function.isApplicable(lastParmType, argType, allowUncheckedConversion, i)) {
|
||||
return ApplicabilityLevel.NOT_APPLICABLE;
|
||||
}
|
||||
}
|
||||
@@ -536,7 +560,9 @@ public final class PsiUtil extends PsiUtilCore {
|
||||
private static boolean areFirstArgumentsApplicable(@NotNull PsiType[] args,
|
||||
@NotNull final PsiParameter[] parms,
|
||||
@NotNull LanguageLevel languageLevel,
|
||||
@NotNull final PsiSubstitutor substitutorForMethod, boolean isRaw) {
|
||||
@NotNull final PsiSubstitutor substitutorForMethod,
|
||||
boolean isRaw,
|
||||
boolean allowUncheckedConversion, ApplicabilityChecker function) {
|
||||
for (int i = 0; i < parms.length - 1; i++) {
|
||||
final PsiType type = args[i];
|
||||
if (type == null) return false;
|
||||
@@ -545,10 +571,11 @@ public final class PsiUtil extends PsiUtilCore {
|
||||
if (isRaw) {
|
||||
final PsiType substErasure = TypeConversionUtil.erasure(substitutedParmType);
|
||||
final PsiType typeErasure = TypeConversionUtil.erasure(type);
|
||||
if (substErasure != null && typeErasure != null && !TypeConversionUtil.isAssignable(substErasure, typeErasure)) {
|
||||
if (substErasure != null && typeErasure != null && !function.isApplicable(substErasure, typeErasure, allowUncheckedConversion, i)) {
|
||||
return false;
|
||||
}
|
||||
} else if (!TypeConversionUtil.isAssignable(substitutedParmType, type)) {
|
||||
}
|
||||
else if (!function.isApplicable(substitutedParmType, type, allowUncheckedConversion, i)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
+2
@@ -54,6 +54,8 @@ public class StrictSubtypingConstraint implements ConstraintFormula {
|
||||
if (PsiType.NULL.equals(myT) || myT == null) return false;
|
||||
if (PsiType.NULL.equals(myS) || myS == null || myT.equalsToText(CommonClassNames.JAVA_LANG_OBJECT)) return true;
|
||||
|
||||
if (PsiType.VOID.equals(myS) ^ PsiType.VOID.equals(myT)) return false;
|
||||
|
||||
InferenceVariable inferenceVariable = session.getInferenceVariable(myS);
|
||||
if (inferenceVariable != null) {
|
||||
inferenceVariable.addBound(myT, InferenceBound.UPPER);
|
||||
|
||||
+43
-74
@@ -33,7 +33,6 @@ import com.intellij.util.containers.HashSet;
|
||||
import gnu.trove.THashMap;
|
||||
import gnu.trove.THashSet;
|
||||
import gnu.trove.TIntArrayList;
|
||||
import gnu.trove.TObjectHashingStrategy;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -444,7 +443,11 @@ public class JavaMethodsConflictResolver implements PsiConflictResolver{
|
||||
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);
|
||||
//process all arguments of varargs call
|
||||
//todo check method reference actual params length
|
||||
final int argsLength = languageLevel.isAtLeast(LanguageLevel.JDK_1_8) && (method1.isVarArgs() || method2.isVarArgs())
|
||||
? getActualParametersLength() : 0;
|
||||
final int max = Math.max(Math.max(params1.length, params2.length), argsLength);
|
||||
PsiType[] types1 = PsiType.createArray(max);
|
||||
PsiType[] types2 = PsiType.createArray(max);
|
||||
final boolean varargsPosition = applicabilityLevel == MethodCandidateInfo.ApplicabilityLevel.VARARGS;
|
||||
@@ -542,63 +545,6 @@ public class JavaMethodsConflictResolver implements PsiConflictResolver{
|
||||
return Specifics.SECOND;
|
||||
}
|
||||
}
|
||||
|
||||
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]);
|
||||
@@ -653,17 +599,44 @@ public class JavaMethodsConflictResolver implements PsiConflictResolver{
|
||||
@NotNull LanguageLevel languageLevel,
|
||||
boolean varargsPosition,
|
||||
@NotNull PsiSubstitutor methodSubstitutor1,
|
||||
PsiMethod method2) {
|
||||
if (languageLevel.isAtLeast(LanguageLevel.JDK_1_8) && method2 != null && method1.getTypeParameters().length > 0 && myArgumentsList instanceof PsiExpressionList) {
|
||||
@NotNull PsiMethod method2) {
|
||||
if (languageLevel.isAtLeast(LanguageLevel.JDK_1_8) && method1.getTypeParameters().length > 0 && myArgumentsList instanceof PsiExpressionList) {
|
||||
final PsiElement parent = myArgumentsList.getParent();
|
||||
if (parent instanceof PsiCallExpression && ((PsiCallExpression)parent).getTypeArguments().length == 0) {
|
||||
return InferenceSession.isMoreSpecific(method2, method1, ((PsiExpressionList)myArgumentsList).getExpressions(), myArgumentsList, varargsPosition);
|
||||
}
|
||||
}
|
||||
final int applicabilityLevel = PsiUtil.getApplicabilityLevel(method1, methodSubstitutor1, types2AtSite, languageLevel, false, varargsPosition);
|
||||
final PsiUtil.ApplicabilityChecker applicabilityChecker = languageLevel.isAtLeast(LanguageLevel.JDK_1_8)
|
||||
? new PsiUtil.ApplicabilityChecker() {
|
||||
@Override
|
||||
public boolean isApplicable(PsiType left, PsiType right,
|
||||
boolean allowUncheckedConversion, int argId) {
|
||||
return isTypeMoreSpecific(left, right, argId);
|
||||
}
|
||||
}
|
||||
: PsiUtil.ApplicabilityChecker.ASSIGNABILITY_CHECKER;
|
||||
final int applicabilityLevel = PsiUtil.getApplicabilityLevel(method1, methodSubstitutor1, types2AtSite, languageLevel, false, varargsPosition, applicabilityChecker);
|
||||
return applicabilityLevel > MethodCandidateInfo.ApplicabilityLevel.NOT_APPLICABLE;
|
||||
}
|
||||
|
||||
// 15.12.2.5
|
||||
// A type S is more specific than a type T for any expression if S <: T (§4.10).
|
||||
// A functional interface type S is more specific than a functional interface type T for
|
||||
// an expression e if T is not a subtype of S and one of the following is true
|
||||
private boolean isTypeMoreSpecific(PsiType left, PsiType right, int argId) {
|
||||
if (TypeConversionUtil.isAssignable(left, right, false)) {
|
||||
return true;
|
||||
}
|
||||
if (myArgumentsList instanceof PsiExpressionList) {
|
||||
final PsiExpression[] expressions = ((PsiExpressionList)myArgumentsList).getExpressions();
|
||||
if (argId < expressions.length) {
|
||||
final Specifics specific = isFunctionalTypeMoreSpecific(expressions[argId], right, left);
|
||||
return Specifics.FIRST.equals(specific);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static PsiType[] typesAtSite(@NotNull PsiType[] types1, @NotNull PsiSubstitutor siteSubstitutor1) {
|
||||
final PsiType[] types = PsiType.createArray(types1.length);
|
||||
@@ -753,18 +726,14 @@ public class JavaMethodsConflictResolver implements PsiConflictResolver{
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static Specifics isFunctionalTypeMoreSpecific(@NotNull CandidateInfo method,
|
||||
@NotNull CandidateInfo conflict,
|
||||
PsiExpression expr,
|
||||
int functionalInterfaceIdx) {
|
||||
private static Specifics isFunctionalTypeMoreSpecific(PsiExpression expr, PsiType sType, PsiType tType) {
|
||||
if (expr instanceof PsiParenthesizedExpression) {
|
||||
return isFunctionalTypeMoreSpecific(method, conflict, ((PsiParenthesizedExpression)expr).getExpression(), functionalInterfaceIdx);
|
||||
return isFunctionalTypeMoreSpecific(((PsiParenthesizedExpression)expr).getExpression(), sType, tType);
|
||||
}
|
||||
|
||||
if (expr instanceof PsiConditionalExpression) {
|
||||
final Specifics thenSpecifics =
|
||||
isFunctionalTypeMoreSpecific(method, conflict, ((PsiConditionalExpression)expr).getThenExpression(), functionalInterfaceIdx);
|
||||
final Specifics elseSpecifics =
|
||||
isFunctionalTypeMoreSpecific(method, conflict, ((PsiConditionalExpression)expr).getElseExpression(), functionalInterfaceIdx);
|
||||
final Specifics thenSpecifics = isFunctionalTypeMoreSpecific(((PsiConditionalExpression)expr).getThenExpression(), sType, tType);
|
||||
final Specifics elseSpecifics = isFunctionalTypeMoreSpecific(((PsiConditionalExpression)expr).getElseExpression(), sType, tType);
|
||||
return thenSpecifics == elseSpecifics ? thenSpecifics : Specifics.NEITHER;
|
||||
}
|
||||
|
||||
@@ -777,9 +746,9 @@ public class JavaMethodsConflictResolver implements PsiConflictResolver{
|
||||
return Specifics.NEITHER;
|
||||
}
|
||||
|
||||
final PsiType sType = getFunctionalType(functionalInterfaceIdx, method);
|
||||
final PsiType tType = getFunctionalType(functionalInterfaceIdx, conflict);
|
||||
if (LambdaUtil.isFunctionalType(sType) && LambdaUtil.isFunctionalType(tType)) {
|
||||
if (LambdaUtil.isFunctionalType(sType) && LambdaUtil.isFunctionalType(tType) &&
|
||||
!TypeConversionUtil.erasure(tType).isAssignableFrom(sType) &&
|
||||
!TypeConversionUtil.erasure(sType).isAssignableFrom(tType)) {
|
||||
final boolean specific12 = InferenceSession.isFunctionalTypeMoreSpecificOnExpression(sType, tType, expr);
|
||||
final boolean specific21 = InferenceSession.isFunctionalTypeMoreSpecificOnExpression(tType, sType, expr);
|
||||
if (specific12 && !specific21) return Specifics.FIRST;
|
||||
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
class ATest {
|
||||
|
||||
private void test(Function<ATest, String> nameF) {
|
||||
Function<ATest, String> aTestStringFunction = nameF.andThen(ATest::withUnderscore);
|
||||
Function<ATest, String> aTestStringFunction1 = nameF.andThen((s) -> {return ATest.withUnderscore(s);});
|
||||
Function<ATest, String> aTestStringFunction2 = nameF.andThen((String s) -> ATest.withUnderscore(s));
|
||||
|
||||
System.out.println(aTestStringFunction);
|
||||
System.out.println(aTestStringFunction1);
|
||||
System.out.println(aTestStringFunction2);
|
||||
}
|
||||
|
||||
static String withUnderscore(String s) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
interface Function<A, R> {
|
||||
|
||||
R apply(A a);
|
||||
|
||||
default <C> Function<A, C> andThen(Function<R, ? extends C> g) { return null;}
|
||||
default Function1V<A> andThen(Function1V<R> g) {return null;}
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
interface Function1V<A> {
|
||||
void apply(A a);
|
||||
}
|
||||
+4
@@ -118,6 +118,10 @@ public class MostSpecificResolutionTest extends LightDaemonAnalyzerTestCase {
|
||||
doTest();
|
||||
}
|
||||
|
||||
public void testFunctionalTypeComparisonWhenMethodsAreNotGeneric() throws Exception {
|
||||
doTest(false);
|
||||
}
|
||||
|
||||
private void doTest() {
|
||||
doTest(true);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user