mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +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:
@@ -216,10 +216,11 @@ public class GenericsUtil {
|
||||
PsiClassType[] extendsTypes = typeParameter.getExtendsListTypes();
|
||||
for (PsiClassType type : extendsTypes) {
|
||||
PsiType extendsType = substitutor.substitute(type);
|
||||
if (!extendsType.isAssignableFrom(substituted)) {
|
||||
return false;
|
||||
if (extendsType.isAssignableFrom(substituted)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (extendsTypes.length > 0) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
+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,
|
||||
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
package pck;
|
||||
|
||||
class A {
|
||||
<T> void foo(T x) {
|
||||
foo(1);
|
||||
|
||||
long x1 = 1L;
|
||||
foo(x1);
|
||||
|
||||
Long x2 = 1L;
|
||||
foo(x2);
|
||||
|
||||
Integer x3 = 1;
|
||||
foo(x3);
|
||||
}
|
||||
|
||||
void foo(long x) {
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
package pck;
|
||||
|
||||
interface I{
|
||||
<T extends Iterable<String> & Cloneable> void foo();
|
||||
}
|
||||
|
||||
abstract class A {
|
||||
abstract <T extends Iterable<String>> void foo();
|
||||
<T extends A & I> void bar(T x){
|
||||
x.foo<error descr="Ambiguous method call: both 'A.foo()' and 'I.foo()' match">()</error>;
|
||||
}
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
package pck;
|
||||
|
||||
class A<T> {}
|
||||
|
||||
interface IA{
|
||||
<T> void foo(A<T> x);
|
||||
}
|
||||
interface IB{
|
||||
<T extends Exception> void foo(A<T> x);
|
||||
}
|
||||
class C {
|
||||
<<error descr="'foo(A<T>)' in 'pck.IB' clashes with 'foo(A<T>)' in 'pck.IA'; both methods have same erasure, yet neither overrides the other"></error>T extends IA & IB> void bar(T x, A<Exception> y){
|
||||
x.foo(y);
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
package pck;
|
||||
|
||||
class B<K> {}
|
||||
class A<K> extends B<K> {
|
||||
void foo(A<A<String>> b){
|
||||
bar<error descr="Ambiguous method call: both 'A.bar(B<? extends A<String>>)' and 'A.bar(A<? extends B<String>>)' match">(b)</error>;
|
||||
}
|
||||
|
||||
<T> void bar(B<? extends A<T>> a){}
|
||||
<T> void bar(A<? extends B<T>> a){}
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
package pck;
|
||||
|
||||
abstract class C{
|
||||
abstract <T extends Comparable<?>> void foo(T x);
|
||||
abstract <T extends Number & Comparable<?>> void foo(T x);
|
||||
void bar(Integer x){
|
||||
foo(x);
|
||||
}
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
interface A
|
||||
{
|
||||
abstract void foo(String[] ... s);
|
||||
}
|
||||
|
||||
interface B
|
||||
{
|
||||
abstract void foo(String[] s);
|
||||
}
|
||||
|
||||
class C<T extends A & B>
|
||||
{
|
||||
void bar(T x)
|
||||
{
|
||||
x.foo(null);
|
||||
}
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
package pck;
|
||||
import static pck.D.foo;
|
||||
import static pck.C.foo;
|
||||
|
||||
public class C {
|
||||
public static <T extends Comparable<S>, S> void foo(T x){}
|
||||
}
|
||||
|
||||
class D {
|
||||
public static <T extends Comparable<?>> void foo(T x){}
|
||||
}
|
||||
|
||||
class B{
|
||||
{
|
||||
foo<error descr="Ambiguous method call: both 'D.foo(Integer)' and 'C.foo(Integer)' match">(1)</error>;
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
package pck;
|
||||
|
||||
import static pck.D.foo;
|
||||
import static pck.C.foo;
|
||||
|
||||
public class C {
|
||||
public static <T> void foo(Comparable<? extends Comparable<T>> x){}
|
||||
}
|
||||
|
||||
class D {
|
||||
public static void foo(Comparable<? extends Number> x){}
|
||||
}
|
||||
|
||||
class B{
|
||||
public static void bar(){
|
||||
foo<error descr="Ambiguous method call: both 'D.foo(Comparable<? extends Number>)' and 'C.foo(Comparable<? extends Comparable<Integer>>)' match">(1)</error>;
|
||||
}
|
||||
}
|
||||
@@ -132,4 +132,36 @@ public class AdvHighlightingJdk7Test extends DaemonAnalyzerTestCase {
|
||||
public void testAmbiguousMultipleTypeParamExtends3() throws Exception {
|
||||
doTestAmbiguous();
|
||||
}
|
||||
|
||||
public void testAmbiguousIDEA57317() throws Exception {
|
||||
doTestAmbiguous();
|
||||
}
|
||||
|
||||
public void testAmbiguousIDEA57278() throws Exception {
|
||||
doTestAmbiguous();
|
||||
}
|
||||
|
||||
public void testAmbiguousIDEA57269() throws Exception {
|
||||
doTestAmbiguous();
|
||||
}
|
||||
|
||||
public void testAmbiguousIDEA67573() throws Exception {
|
||||
doTestAmbiguous();
|
||||
}
|
||||
|
||||
public void testAmbiguousIDEA57306() throws Exception {
|
||||
doTestAmbiguous();
|
||||
}
|
||||
|
||||
public void testAmbiguousIDEA57535() throws Exception {
|
||||
doTestAmbiguous();
|
||||
}
|
||||
|
||||
public void testAmbiguousIDEA67832() throws Exception {
|
||||
doTestAmbiguous();
|
||||
}
|
||||
|
||||
public void testAmbiguousIDEA67837() throws Exception {
|
||||
doTestAmbiguous();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user