mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-18 09:34:34 +07:00
ambiguous method calls: conflict resolver, tests
(IDEA-67832; IDEA-67837; IDEA-67573; IDEA-57306; IDEA-57535; IDEA-57269; IDEA-57278; IDEA-57317)
This commit is contained in:
+98
-44
@@ -31,10 +31,7 @@ import gnu.trove.THashSet;
|
||||
import gnu.trove.TIntArrayList;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Created by IntelliJ IDEA.
|
||||
@@ -281,7 +278,7 @@ public class JavaMethodsConflictResolver implements PsiConflictResolver{
|
||||
@MethodCandidateInfo.ApplicabilityLevelConstant int maxApplicabilityLevel = 0;
|
||||
boolean toFilter = false;
|
||||
for (CandidateInfo conflict : conflicts) {
|
||||
@MethodCandidateInfo.ApplicabilityLevelConstant final int level = ((MethodCandidateInfo)conflict).getApplicabilityLevel();
|
||||
final @MethodCandidateInfo.ApplicabilityLevelConstant int level = preferVarargs((MethodCandidateInfo)conflict);
|
||||
if (maxApplicabilityLevel > 0 && maxApplicabilityLevel != level) {
|
||||
toFilter = true;
|
||||
}
|
||||
@@ -293,7 +290,7 @@ public class JavaMethodsConflictResolver implements PsiConflictResolver{
|
||||
if (toFilter) {
|
||||
for (Iterator<CandidateInfo> iterator = conflicts.iterator(); iterator.hasNext();) {
|
||||
CandidateInfo info = iterator.next();
|
||||
final int level = ((MethodCandidateInfo)info).getApplicabilityLevel(); //cached
|
||||
final int level = preferVarargs(info);
|
||||
if (level < maxApplicabilityLevel) {
|
||||
iterator.remove();
|
||||
}
|
||||
@@ -303,6 +300,17 @@ public class JavaMethodsConflictResolver implements PsiConflictResolver{
|
||||
return maxApplicabilityLevel;
|
||||
}
|
||||
|
||||
private static int preferVarargs(CandidateInfo info) {
|
||||
final int level = ((MethodCandidateInfo)info).getApplicabilityLevel();
|
||||
if (level == MethodCandidateInfo.ApplicabilityLevel.FIXED_ARITY) {
|
||||
final PsiMethod psiMethod = (PsiMethod)info.getElement();
|
||||
if (psiMethod != null && psiMethod.isVarArgs() && JavaVersionService.getInstance().isAtLeast(psiMethod, JavaSdkVersion.JDK_1_7)) {
|
||||
return level + 1;
|
||||
}
|
||||
}
|
||||
return level;
|
||||
}
|
||||
|
||||
private static int getCheckLevel(MethodCandidateInfo method){
|
||||
boolean visible = method.isAccessible();// && !method.myStaticProblem;
|
||||
boolean available = method.isStaticsScopeCorrect();
|
||||
@@ -318,7 +326,15 @@ public class JavaMethodsConflictResolver implements PsiConflictResolver{
|
||||
}
|
||||
|
||||
private static Specifics checkSubtyping(PsiType type1, PsiType type2, PsiMethod method1, PsiMethod method2) {
|
||||
boolean noBoxing = type1 instanceof PsiPrimitiveType == type2 instanceof PsiPrimitiveType;
|
||||
return checkSubtyping(type1, type2, method1, method2, true);
|
||||
}
|
||||
|
||||
private static Specifics checkSubtyping(PsiType type1,
|
||||
PsiType type2,
|
||||
PsiMethod method1,
|
||||
PsiMethod method2,
|
||||
boolean boxingHappening) {
|
||||
boolean noBoxing = boxingHappening || type1 instanceof PsiPrimitiveType == type2 instanceof PsiPrimitiveType;
|
||||
final boolean allowUncheckedConversion =
|
||||
!method1.hasModifierProperty(PsiModifier.STATIC) && !method2.hasModifierProperty(PsiModifier.STATIC) ||
|
||||
method1.getContainingClass() == method2.getContainingClass();
|
||||
@@ -358,8 +374,6 @@ public class JavaMethodsConflictResolver implements PsiConflictResolver{
|
||||
final PsiTypeParameter[] typeParameters2 = method2.getTypeParameters();
|
||||
final PsiSubstitutor classSubstitutor1 = info1.getSubstitutor(); //substitutions for method type parameters will be ignored
|
||||
final PsiSubstitutor classSubstitutor2 = info2.getSubstitutor();
|
||||
PsiSubstitutor methodSubstitutor1 = PsiSubstitutor.EMPTY;
|
||||
PsiSubstitutor methodSubstitutor2 = PsiSubstitutor.EMPTY;
|
||||
|
||||
final int max = Math.max(params1.length, params2.length);
|
||||
PsiType[] types1 = new PsiType[max];
|
||||
@@ -382,26 +396,10 @@ public class JavaMethodsConflictResolver implements PsiConflictResolver{
|
||||
types2[i] = type2;
|
||||
}
|
||||
|
||||
if (typeParameters1.length == 0 || typeParameters2.length == 0) {
|
||||
if (typeParameters1.length > 0) {
|
||||
final PsiResolveHelper resolveHelper = JavaPsiFacade.getInstance(myArgumentsList.getProject()).getResolveHelper();
|
||||
methodSubstitutor1 = calculateMethodSubstitutor(typeParameters1, types1, types2, resolveHelper);
|
||||
}
|
||||
else if (typeParameters2.length > 0) {
|
||||
final PsiResolveHelper resolveHelper = JavaPsiFacade.getInstance(myArgumentsList.getProject()).getResolveHelper();
|
||||
methodSubstitutor2 = calculateMethodSubstitutor(typeParameters2, types2, types1, resolveHelper);
|
||||
}
|
||||
}
|
||||
else {
|
||||
PsiElementFactory factory = JavaPsiFacade.getInstance(myArgumentsList.getProject()).getElementFactory();
|
||||
methodSubstitutor1 = factory.createRawSubstitutor(PsiSubstitutor.EMPTY, typeParameters1);
|
||||
methodSubstitutor2 = factory.createRawSubstitutor(PsiSubstitutor.EMPTY, typeParameters2);
|
||||
}
|
||||
|
||||
int[] boxingHappened = new int[2];
|
||||
for (int i = 0; i < types1.length; i++) {
|
||||
PsiType type1 = classSubstitutor1.substitute(methodSubstitutor1.substitute(types1[i]));
|
||||
PsiType type2 = classSubstitutor2.substitute(methodSubstitutor2.substitute(types2[i]));
|
||||
PsiType type1 = classSubstitutor1.substitute(types1[i]);
|
||||
PsiType type2 = classSubstitutor2.substitute(types2[i]);
|
||||
PsiType argType = i < myActualParameterTypes.length ? myActualParameterTypes[i] : null;
|
||||
|
||||
boxingHappened[0] += isBoxingHappened(argType, type1) ? 1 : 0;
|
||||
@@ -412,15 +410,31 @@ public class JavaMethodsConflictResolver implements PsiConflictResolver{
|
||||
|
||||
Specifics isMoreSpecific = null;
|
||||
for (int i = 0; i < types1.length; i++) {
|
||||
PsiType type1 = classSubstitutor1.substitute(methodSubstitutor1.substitute(types1[i]));
|
||||
PsiType type2 = classSubstitutor2.substitute(methodSubstitutor2.substitute(types2[i]));
|
||||
|
||||
Specifics specifics = type1 == null || type2 == null ? null : checkSubtyping(type1, type2, method1, method2);
|
||||
Specifics specifics = checkSubstitutorSpecific(method1, method2, classSubstitutor1, classSubstitutor2, types1[i], types2[i], isMoreSpecific);
|
||||
if (specifics == null) {
|
||||
if (Comparing.equal(type1, type2)) {
|
||||
specifics = checkSubstitutorSpecific(method1, method2, classSubstitutor1, classSubstitutor2, types1[i], types2[i]);
|
||||
PsiSubstitutor methodSubstitutor1 = PsiSubstitutor.EMPTY;
|
||||
PsiSubstitutor methodSubstitutor2 = PsiSubstitutor.EMPTY;
|
||||
if (typeParameters1.length == 0 || typeParameters2.length == 0) {
|
||||
if (typeParameters1.length > 0) {
|
||||
final PsiResolveHelper resolveHelper = JavaPsiFacade.getInstance(myArgumentsList.getProject()).getResolveHelper();
|
||||
methodSubstitutor1 = calculateMethodSubstitutor(typeParameters1, types1, types2, resolveHelper);
|
||||
}
|
||||
else if (typeParameters2.length > 0) {
|
||||
final PsiResolveHelper resolveHelper = JavaPsiFacade.getInstance(myArgumentsList.getProject()).getResolveHelper();
|
||||
methodSubstitutor2 = calculateMethodSubstitutor(typeParameters2, types2, types1, resolveHelper);
|
||||
}
|
||||
}
|
||||
else {
|
||||
PsiElementFactory factory = JavaPsiFacade.getInstance(myArgumentsList.getProject()).getElementFactory();
|
||||
methodSubstitutor1 = factory.createRawSubstitutor(PsiSubstitutor.EMPTY, typeParameters1);
|
||||
methodSubstitutor2 = factory.createRawSubstitutor(PsiSubstitutor.EMPTY, typeParameters2);
|
||||
}
|
||||
PsiType type1 = classSubstitutor1.substitute(methodSubstitutor1.substitute(types1[i]));
|
||||
PsiType type2 = classSubstitutor2.substitute(methodSubstitutor2.substitute(types2[i]));
|
||||
specifics = type1 == null || type2 == null ? null : checkSubtyping(type1, type2, method1, method2, boxingHappened[0] == 0 || boxingHappened[1] == 0);
|
||||
if (specifics == null) {
|
||||
continue;
|
||||
}
|
||||
if (specifics == null) continue;
|
||||
}
|
||||
|
||||
switch (specifics) {
|
||||
@@ -473,7 +487,7 @@ public class JavaMethodsConflictResolver implements PsiConflictResolver{
|
||||
PsiSubstitutor classSubstitutor1,
|
||||
PsiSubstitutor classSubstitutor2,
|
||||
PsiType type1,
|
||||
PsiType type2) {
|
||||
PsiType type2, Specifics moreSpecific) {
|
||||
final Map<PsiTypeParameter, PsiType> map1 = classSubstitutor1.getSubstitutionMap();
|
||||
final Map<PsiTypeParameter, PsiType> map2 = classSubstitutor2.getSubstitutionMap();
|
||||
|
||||
@@ -491,20 +505,60 @@ public class JavaMethodsConflictResolver implements PsiConflictResolver{
|
||||
else {
|
||||
final PsiTypeParameter p1 = map1.keySet().iterator().next();
|
||||
final PsiTypeParameter p2 = map2.keySet().iterator().next();
|
||||
Specifics specifics = checkSubtyping(TypeConversionUtil.erasure(PsiSubstitutor.EMPTY.substitute(p1)),
|
||||
TypeConversionUtil.erasure(PsiSubstitutor.EMPTY.substitute(p2)), method1, method2);
|
||||
if (specifics != null) {
|
||||
return specifics;
|
||||
} else {
|
||||
final PsiType ctype1 = classSubstitutor1.substitute(type1);
|
||||
final PsiType ctype2 = classSubstitutor2.substitute(type2);
|
||||
return checkSubtyping(ctype1, ctype2, method1, method2);
|
||||
}
|
||||
return checkTypeParams(method1, method2, classSubstitutor1, classSubstitutor2, type1, type2, p1, p2, moreSpecific);
|
||||
}
|
||||
} else {
|
||||
final PsiClass aClass1 = PsiUtil.resolveClassInClassTypeOnly(type1);
|
||||
final PsiClass aClass2 = PsiUtil.resolveClassInClassTypeOnly(type2);
|
||||
if (aClass1 instanceof PsiTypeParameter && aClass2 instanceof PsiTypeParameter) {
|
||||
return checkTypeParams(method1, method2, classSubstitutor1, classSubstitutor2, type1, type2, (PsiTypeParameter)aClass1, (PsiTypeParameter)aClass2,
|
||||
moreSpecific);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static Specifics checkTypeParams(PsiMethod method1,
|
||||
PsiMethod method2,
|
||||
PsiSubstitutor classSubstitutor1,
|
||||
PsiSubstitutor classSubstitutor2,
|
||||
PsiType type1,
|
||||
PsiType type2,
|
||||
PsiTypeParameter p1,
|
||||
PsiTypeParameter p2, Specifics moreSpecific) {
|
||||
Specifics specifics = checkSubtyping(TypeConversionUtil.erasure(PsiSubstitutor.EMPTY.substitute(p1)),
|
||||
TypeConversionUtil.erasure(PsiSubstitutor.EMPTY.substitute(p2)), method1, method2);
|
||||
if (specifics == Specifics.NEITHER) {
|
||||
final Set<PsiElement> resolved1 = new HashSet<PsiElement>();
|
||||
for (PsiJavaCodeReferenceElement referenceElement : p1.getExtendsList().getReferenceElements()) {
|
||||
final PsiElement resolve = referenceElement.resolve();
|
||||
if (resolve != null) {
|
||||
resolved1.add(resolve);
|
||||
}
|
||||
}
|
||||
|
||||
final Set<PsiElement> resolved2 = new HashSet<PsiElement>();
|
||||
for (PsiJavaCodeReferenceElement referenceElement : p2.getExtendsList().getReferenceElements()) {
|
||||
final PsiElement resolve = referenceElement.resolve();
|
||||
if (resolve != null) {
|
||||
resolved2.add(resolve);
|
||||
}
|
||||
}
|
||||
|
||||
if (resolved1.size() > resolved2.size() && resolved1.containsAll(resolved2)) return Specifics.FIRST;
|
||||
if (resolved2.size() > resolved1.size() && resolved2.containsAll(resolved1)) return Specifics.SECOND;
|
||||
}
|
||||
if (specifics != null) {
|
||||
return specifics;
|
||||
} else {
|
||||
final PsiType ctype1 = classSubstitutor1.substitute(type1);
|
||||
final PsiType ctype2 = classSubstitutor2.substitute(type2);
|
||||
specifics = checkSubtyping(ctype1, ctype2, method1, method2);
|
||||
return specifics == null && moreSpecific == null ? Specifics.NEITHER : specifics;
|
||||
}
|
||||
}
|
||||
|
||||
private PsiSubstitutor calculateMethodSubstitutor(final PsiTypeParameter[] typeParameters,
|
||||
final PsiType[] types1,
|
||||
final PsiType[] types2,
|
||||
|
||||
Reference in New Issue
Block a user