mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-04-21 22:11:40 +07:00
more specific: lambda extended initial fix (IDEA-113357)
This commit is contained in:
@@ -23,6 +23,7 @@ import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.pom.java.LanguageLevel;
|
||||
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
|
||||
import com.intellij.psi.infos.CandidateInfo;
|
||||
import com.intellij.psi.infos.ClassCandidateInfo;
|
||||
import com.intellij.psi.infos.MethodCandidateInfo;
|
||||
import com.intellij.psi.util.*;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
@@ -648,144 +649,6 @@ public class LambdaUtil {
|
||||
return result;
|
||||
}
|
||||
|
||||
public static void checkMoreSpecificReturnType(List<CandidateInfo> conflicts, PsiType[] actualParameterTypes) {
|
||||
final CandidateInfo[] newConflictsArray = conflicts.toArray(new CandidateInfo[conflicts.size()]);
|
||||
for (int i = 1; i < newConflictsArray.length; i++) {
|
||||
final CandidateInfo method = newConflictsArray[i];
|
||||
for (int j = 0; j < i; j++) {
|
||||
final CandidateInfo conflict = newConflictsArray[j];
|
||||
assert conflict != method;
|
||||
int moreSpecific = 0;
|
||||
final PsiMethod methodElement = (PsiMethod)method.getElement();
|
||||
final PsiMethod conflictElement = (PsiMethod)conflict.getElement();
|
||||
if (methodElement.isVarArgs() == conflictElement.isVarArgs()) {
|
||||
for (int functionalInterfaceIdx = 0; functionalInterfaceIdx < actualParameterTypes.length; functionalInterfaceIdx++) {
|
||||
final PsiType interfaceReturnType = getReturnType(functionalInterfaceIdx, method);
|
||||
final PsiType interfaceReturnType1 = getReturnType(functionalInterfaceIdx, conflict);
|
||||
if (actualParameterTypes[functionalInterfaceIdx] instanceof PsiLambdaExpressionType || actualParameterTypes[functionalInterfaceIdx] instanceof PsiMethodReferenceType) {
|
||||
if (interfaceReturnType != null && interfaceReturnType1 != null && !Comparing.equal(interfaceReturnType, interfaceReturnType1)) {
|
||||
int moreSpecific1 = isMoreSpecific(interfaceReturnType, interfaceReturnType1, actualParameterTypes[functionalInterfaceIdx]);
|
||||
if (moreSpecific < 0 && moreSpecific1 > 0 || moreSpecific > 0 && moreSpecific1 < 0) {
|
||||
moreSpecific = 0;
|
||||
break;
|
||||
}
|
||||
moreSpecific = moreSpecific1;
|
||||
}
|
||||
} else if (interfaceReturnType != null && interfaceReturnType1 != null) {
|
||||
moreSpecific = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (moreSpecific > 0 && conflictElement.getParameterList().getParametersCount() <= actualParameterTypes.length) {
|
||||
conflicts.remove(method);
|
||||
break;
|
||||
}
|
||||
else if (moreSpecific < 0 && methodElement.getParameterList().getParametersCount() <= actualParameterTypes.length) {
|
||||
conflicts.remove(conflict);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum TypeKind {
|
||||
PRIMITIVE, REFERENCE, NONE_DETERMINED
|
||||
}
|
||||
|
||||
private static int isMoreSpecific(PsiType returnType, PsiType returnType1, PsiType lambdaType) {
|
||||
TypeKind typeKind = TypeKind.PRIMITIVE;
|
||||
if (lambdaType instanceof PsiLambdaExpressionType) {
|
||||
typeKind = areLambdaReturnExpressionsPrimitive((PsiLambdaExpressionType)lambdaType);
|
||||
} else if (lambdaType instanceof PsiMethodReferenceType) {
|
||||
final PsiElement referencedElement = ((PsiMethodReferenceType)lambdaType).getExpression().resolve();
|
||||
if (referencedElement instanceof PsiMethod && !(((PsiMethod)referencedElement).getReturnType() instanceof PsiPrimitiveType)) {
|
||||
typeKind = TypeKind.REFERENCE;
|
||||
}
|
||||
}
|
||||
if (typeKind != TypeKind.NONE_DETERMINED) {
|
||||
if (returnType instanceof PsiPrimitiveType) {
|
||||
final int moreSpecific = typeKind == TypeKind.PRIMITIVE ? 1 : -1;
|
||||
if (!(returnType1 instanceof PsiPrimitiveType)) {
|
||||
return -moreSpecific;
|
||||
} else {
|
||||
return TypeConversionUtil.isAssignable(returnType, returnType1) ? moreSpecific : -moreSpecific;
|
||||
}
|
||||
}
|
||||
if (returnType1 instanceof PsiPrimitiveType) {
|
||||
return typeKind == TypeKind.PRIMITIVE ? 1 : -1;
|
||||
}
|
||||
}
|
||||
|
||||
final PsiClassType.ClassResolveResult r = PsiUtil.resolveGenericsClassInType(GenericsUtil.eliminateWildcards(returnType, false));
|
||||
final PsiClass rClass = r.getElement();
|
||||
final PsiClassType.ClassResolveResult r1 = PsiUtil.resolveGenericsClassInType(GenericsUtil.eliminateWildcards(returnType1, false));
|
||||
final PsiClass rClass1 = r1.getElement();
|
||||
if (rClass != null && rClass1 != null) {
|
||||
if (rClass == rClass1) {
|
||||
int moreSpecific = 0;
|
||||
for (PsiTypeParameter parameter : rClass.getTypeParameters()) {
|
||||
final PsiType t = r.getSubstitutor().substituteWithBoundsPromotion(parameter);
|
||||
final PsiType t1 = r1.getSubstitutor().substituteWithBoundsPromotion(parameter);
|
||||
if (t == null || t1 == null) continue;
|
||||
if (t1.isAssignableFrom(t)) {
|
||||
if (moreSpecific == 1) {
|
||||
return 0;
|
||||
}
|
||||
moreSpecific = -1;
|
||||
}
|
||||
else if (t.isAssignableFrom(t1)) {
|
||||
if (moreSpecific == -1) {
|
||||
return 0;
|
||||
}
|
||||
moreSpecific = 1;
|
||||
}
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return moreSpecific;
|
||||
}
|
||||
else if (rClass1.isInheritor(rClass, true)) {
|
||||
return 1;
|
||||
}
|
||||
else if (rClass.isInheritor(rClass1, true)) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static TypeKind areLambdaReturnExpressionsPrimitive(PsiLambdaExpressionType lambdaType) {
|
||||
final List<PsiExpression> returnExpressions = getReturnExpressions(lambdaType.getExpression());
|
||||
TypeKind typeKind = TypeKind.NONE_DETERMINED;
|
||||
for (PsiExpression expression : returnExpressions) {
|
||||
final PsiType returnExprType = expression.getType();
|
||||
if (returnExprType instanceof PsiPrimitiveType) {
|
||||
if (typeKind == TypeKind.REFERENCE) {
|
||||
typeKind = TypeKind.NONE_DETERMINED;
|
||||
break;
|
||||
}
|
||||
typeKind = TypeKind.PRIMITIVE;
|
||||
} else {
|
||||
if (typeKind == TypeKind.PRIMITIVE) {
|
||||
typeKind = TypeKind.NONE_DETERMINED;
|
||||
break;
|
||||
}
|
||||
typeKind = TypeKind.REFERENCE;
|
||||
}
|
||||
}
|
||||
return typeKind;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PsiType getReturnType(int functionalTypeIdx, CandidateInfo method) {
|
||||
final PsiParameter[] methodParameters = ((PsiMethod)method.getElement()).getParameterList().getParameters();
|
||||
if (methodParameters.length == 0) return null;
|
||||
final PsiParameter param = functionalTypeIdx < methodParameters.length ? methodParameters[functionalTypeIdx] : methodParameters[methodParameters.length - 1];
|
||||
final PsiType functionalInterfaceType = method.getSubstitutor().substitute(param.getType());
|
||||
return getFunctionalInterfaceReturnType(functionalInterfaceType);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static String checkFunctionalInterface(@NotNull PsiAnnotation annotation, @NotNull LanguageLevel languageLevel) {
|
||||
if (languageLevel.isAtLeast(LanguageLevel.JDK_1_8) && Comparing.strEqual(annotation.getQualifiedName(), JAVA_LANG_FUNCTIONAL_INTERFACE)) {
|
||||
|
||||
@@ -33,6 +33,7 @@ import gnu.trove.THashMap;
|
||||
import gnu.trove.THashSet;
|
||||
import gnu.trove.TIntArrayList;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
@@ -140,7 +141,7 @@ public class JavaMethodsConflictResolver implements PsiConflictResolver{
|
||||
}
|
||||
}
|
||||
}
|
||||
LambdaUtil.checkMoreSpecificReturnType(conflicts, myActualParameterTypes);
|
||||
checkMoreSpecificReturnType(conflicts, myActualParameterTypes, languageLevel);
|
||||
}
|
||||
|
||||
public void checkSpecifics(@NotNull List<CandidateInfo> conflicts,
|
||||
@@ -647,4 +648,150 @@ public class JavaMethodsConflictResolver implements PsiConflictResolver{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum TypeKind {
|
||||
PRIMITIVE, REFERENCE, NONE_DETERMINED
|
||||
}
|
||||
|
||||
public void checkMoreSpecificReturnType(List<CandidateInfo> conflicts, PsiType[] actualParameterTypes, LanguageLevel languageLevel) {
|
||||
final CandidateInfo[] newConflictsArray = conflicts.toArray(new CandidateInfo[conflicts.size()]);
|
||||
next:
|
||||
for (int i = 1; i < newConflictsArray.length; i++) {
|
||||
final CandidateInfo method = newConflictsArray[i];
|
||||
for (int j = 0; j < i; j++) {
|
||||
final CandidateInfo conflict = newConflictsArray[j];
|
||||
assert conflict != method;
|
||||
switch (isMoreSpecific(method, conflict, actualParameterTypes, languageLevel)) {
|
||||
case FIRST:
|
||||
conflicts.remove(conflict);
|
||||
break;
|
||||
case SECOND:
|
||||
conflicts.remove(method);
|
||||
continue next;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static Specifics isMoreSpecific(CandidateInfo method,
|
||||
CandidateInfo conflict,
|
||||
PsiType[] actualParameterTypes,
|
||||
LanguageLevel languageLevel) {
|
||||
Specifics moreSpecific = Specifics.NEITHER;
|
||||
final PsiMethod methodElement = (PsiMethod)method.getElement();
|
||||
final PsiMethod conflictElement = (PsiMethod)conflict.getElement();
|
||||
if (methodElement != null &&
|
||||
conflictElement != null &&
|
||||
methodElement.isVarArgs() == conflictElement.isVarArgs() &&
|
||||
methodElement.getParameterList().getParametersCount() <= actualParameterTypes.length &&
|
||||
conflictElement.getParameterList().getParametersCount() <= actualParameterTypes.length) {
|
||||
for (int functionalInterfaceIdx = 0; functionalInterfaceIdx < actualParameterTypes.length; functionalInterfaceIdx++) {
|
||||
final PsiType interfaceReturnType = getReturnType(functionalInterfaceIdx, method);
|
||||
final PsiType interfaceReturnType1 = getReturnType(functionalInterfaceIdx, conflict);
|
||||
if (actualParameterTypes[functionalInterfaceIdx] instanceof PsiLambdaExpressionType || actualParameterTypes[functionalInterfaceIdx] instanceof PsiMethodReferenceType) {
|
||||
if (interfaceReturnType != null && interfaceReturnType1 != null && !Comparing.equal(interfaceReturnType, interfaceReturnType1)) {
|
||||
|
||||
final TypeKind typeKind = getKind(actualParameterTypes[functionalInterfaceIdx]);
|
||||
Specifics moreSpecific1 = Specifics.NEITHER;
|
||||
if (typeKind != TypeKind.NONE_DETERMINED) {
|
||||
final boolean isPrimitive = typeKind == TypeKind.PRIMITIVE;
|
||||
if (interfaceReturnType instanceof PsiPrimitiveType) {
|
||||
if (interfaceReturnType1 instanceof PsiPrimitiveType &&
|
||||
TypeConversionUtil.isAssignable(interfaceReturnType, interfaceReturnType1)) {
|
||||
moreSpecific1 = isPrimitive ? Specifics.SECOND : Specifics.FIRST;
|
||||
} else {
|
||||
moreSpecific1 = isPrimitive ? Specifics.FIRST : Specifics.SECOND;
|
||||
}
|
||||
} else if (interfaceReturnType1 instanceof PsiPrimitiveType) {
|
||||
moreSpecific1 = isPrimitive ? Specifics.SECOND : Specifics.FIRST;
|
||||
}
|
||||
}
|
||||
|
||||
if (moreSpecific1 == Specifics.NEITHER && (interfaceReturnType != PsiType.VOID && interfaceReturnType1 != PsiType.VOID)) {
|
||||
final PsiSubstitutor siteSubstitutor1 = ((MethodCandidateInfo)method).getSiteSubstitutor();
|
||||
final PsiSubstitutor siteSubstitutor2 = ((MethodCandidateInfo)conflict).getSiteSubstitutor();
|
||||
|
||||
final PsiTypeParameter[] typeParameters1 = methodElement.getTypeParameters();
|
||||
final PsiTypeParameter[] typeParameters2 = conflictElement.getTypeParameters();
|
||||
|
||||
final PsiType[] types1AtSite = {interfaceReturnType1};
|
||||
final PsiType[] types2AtSite = {interfaceReturnType};
|
||||
|
||||
final PsiSubstitutor methodSubstitutor1 = calculateMethodSubstitutor(typeParameters1, methodElement, siteSubstitutor1, types2AtSite, types1AtSite, languageLevel);
|
||||
final PsiSubstitutor methodSubstitutor2 = calculateMethodSubstitutor(typeParameters2, conflictElement, siteSubstitutor2, types1AtSite, types2AtSite,languageLevel);
|
||||
|
||||
final boolean applicable12 = TypeConversionUtil.isAssignable(interfaceReturnType1, methodSubstitutor1.substitute(interfaceReturnType));
|
||||
final boolean applicable21 = TypeConversionUtil.isAssignable(interfaceReturnType, methodSubstitutor2.substitute(interfaceReturnType1));
|
||||
|
||||
|
||||
if (applicable12 || applicable21) {
|
||||
if (!applicable21) {
|
||||
moreSpecific1 = Specifics.FIRST;
|
||||
}
|
||||
|
||||
if (!applicable12) {
|
||||
moreSpecific1 = Specifics.SECOND;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (moreSpecific != Specifics.NEITHER && moreSpecific != moreSpecific1) {
|
||||
return Specifics.NEITHER;
|
||||
}
|
||||
|
||||
moreSpecific = moreSpecific1;
|
||||
}
|
||||
} else if (interfaceReturnType != null && interfaceReturnType1 != null) {
|
||||
return Specifics.NEITHER;
|
||||
}
|
||||
}
|
||||
}
|
||||
return moreSpecific;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PsiType getReturnType(int functionalTypeIdx, CandidateInfo method) {
|
||||
final PsiParameter[] methodParameters = ((PsiMethod)method.getElement()).getParameterList().getParameters();
|
||||
if (methodParameters.length == 0) return null;
|
||||
final PsiParameter param = functionalTypeIdx < methodParameters.length ? methodParameters[functionalTypeIdx] : methodParameters[methodParameters.length - 1];
|
||||
final PsiType functionalInterfaceType = ((MethodCandidateInfo)method).getSiteSubstitutor().substitute(param.getType());
|
||||
return LambdaUtil.getFunctionalInterfaceReturnType(functionalInterfaceType);
|
||||
}
|
||||
|
||||
private static TypeKind getKind(PsiType lambdaType) {
|
||||
TypeKind typeKind = TypeKind.PRIMITIVE;
|
||||
if (lambdaType instanceof PsiLambdaExpressionType) {
|
||||
typeKind = areLambdaReturnExpressionsPrimitive((PsiLambdaExpressionType)lambdaType);
|
||||
} else if (lambdaType instanceof PsiMethodReferenceType) {
|
||||
final PsiElement referencedElement = ((PsiMethodReferenceType)lambdaType).getExpression().resolve();
|
||||
if (referencedElement instanceof PsiMethod && !(((PsiMethod)referencedElement).getReturnType() instanceof PsiPrimitiveType)) {
|
||||
typeKind = TypeKind.REFERENCE;
|
||||
}
|
||||
}
|
||||
return typeKind;
|
||||
}
|
||||
|
||||
private static TypeKind areLambdaReturnExpressionsPrimitive(PsiLambdaExpressionType lambdaType) {
|
||||
final List<PsiExpression> returnExpressions = LambdaUtil.getReturnExpressions(lambdaType.getExpression());
|
||||
TypeKind typeKind = TypeKind.NONE_DETERMINED;
|
||||
for (PsiExpression expression : returnExpressions) {
|
||||
final PsiType returnExprType = expression.getType();
|
||||
if (returnExprType instanceof PsiPrimitiveType) {
|
||||
if (typeKind == TypeKind.REFERENCE) {
|
||||
typeKind = TypeKind.NONE_DETERMINED;
|
||||
break;
|
||||
}
|
||||
typeKind = TypeKind.PRIMITIVE;
|
||||
} else {
|
||||
if (typeKind == TypeKind.PRIMITIVE) {
|
||||
typeKind = TypeKind.NONE_DETERMINED;
|
||||
break;
|
||||
}
|
||||
typeKind = TypeKind.REFERENCE;
|
||||
}
|
||||
}
|
||||
return typeKind;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,6 @@ class AmbiguityRawGenerics {
|
||||
<Z> void foo(I3<Z> s) { }
|
||||
|
||||
void bar() {
|
||||
foo<error descr="Ambiguous method call: both 'AmbiguityRawGenerics.foo(I)' and 'AmbiguityRawGenerics.foo(I1)' match">(()-> { throw new RuntimeException(); })</error>;
|
||||
foo<error descr="Ambiguous method call: both 'AmbiguityRawGenerics.foo(I)' and 'AmbiguityRawGenerics.foo(I3<Object>)' match">(()-> { throw new RuntimeException(); })</error>;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
class A {
|
||||
private interface AsyncFunction<I, O> {
|
||||
Promise<O> apply(I input);
|
||||
}
|
||||
|
||||
private interface Function<I, O> {
|
||||
O apply(I input);
|
||||
}
|
||||
|
||||
private interface Promise<V> {
|
||||
<T1> Promise<T1> then(Function<? super V, T1> function);
|
||||
<T2> Promise<T2> then(AsyncFunction<? super V, T2> function);
|
||||
}
|
||||
|
||||
private static Promise<Integer> calculateLength(String word) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void main(Promise<String> helloWorld) {
|
||||
helloWorld.then(A::calculateLength);
|
||||
}
|
||||
}
|
||||
|
||||
class AAmbiguous {
|
||||
private interface AsyncFunction<I, O> {
|
||||
O apply(I input);
|
||||
}
|
||||
|
||||
private interface Function<I, O> {
|
||||
O apply(I input);
|
||||
}
|
||||
|
||||
private interface Promise<V> {
|
||||
<T1> Promise<T1> then(Function<? super V, T1> function);
|
||||
<T2> Promise<T2> then(AsyncFunction<? super V, T2> function);
|
||||
}
|
||||
|
||||
private static Promise<Integer> calculateLength(String word) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void main(Promise<String> helloWorld) {
|
||||
helloWorld.then<error descr="Ambiguous method call: both 'Promise.then(Function<? super String,Promise<Integer>>)' and 'Promise.then(AsyncFunction<? super String,Promise<Integer>>)' match">(AAmbiguous::calculateLength)</error>;
|
||||
}
|
||||
}
|
||||
@@ -95,7 +95,7 @@ class MyTest2 {
|
||||
System.out.println(i);
|
||||
}
|
||||
|
||||
private static void m(I2 i) {
|
||||
private static void <warning descr="Private method 'm(MyTest2.I2)' is never used">m</warning>(I2 i) {
|
||||
System.out.println(i);
|
||||
}
|
||||
|
||||
@@ -104,6 +104,6 @@ class MyTest2 {
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
m(Foo::new);
|
||||
m<error descr="Ambiguous method call: both 'MyTest2.m(I2)' and 'MyTest2.m(I3)' match">(Foo::new)</error>;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,6 +91,7 @@ public class LambdaHighlightingTest extends LightDaemonAnalyzerTestCase {
|
||||
public void testDiamondInference() { doTest();}
|
||||
public void testFunctionalInterfaceCheck() { doTest();}
|
||||
public void testUnderscores() { doTest(true);}
|
||||
public void testReturnTypeAmbiguity() { doTest();}
|
||||
|
||||
private void doTest() {
|
||||
doTest(false);
|
||||
|
||||
Reference in New Issue
Block a user