ambiguity of implicit constructor call (IDEA-160248)

This commit is contained in:
Anna Kozlova
2016-08-25 17:36:03 +03:00
parent 62760b94f7
commit 5774dbf0cd
3 changed files with 86 additions and 0 deletions
@@ -528,6 +528,11 @@ public class HighlightClassUtil {
PsiMethod[] constructors = baseClass.getConstructors();
if (constructors.length == 0) return null;
final HighlightInfo highlightInfo = constructors.length > 1 ? checkAmbiguityOfImplicitConstructorCall(constructors, range) : null;
if (highlightInfo != null) {
return highlightInfo;
}
for (PsiMethod constructor : constructors) {
if (resolveHelper.isAccessible(constructor, aClass, null)) {
if (constructor.getParameterList().getParametersCount() == 0 ||
@@ -556,6 +561,55 @@ public class HighlightClassUtil {
return info;
}
@Nullable
private static HighlightInfo checkAmbiguityOfImplicitConstructorCall(PsiMethod[] constructors, TextRange range) {
List<PsiMethod> varargConstructors = new ArrayList<>();
for (PsiMethod constructor : constructors) {
final PsiParameter[] parameters = constructor.getParameterList().getParameters();
if (parameters.length == 0) {
varargConstructors.clear();
break;
}
if (parameters.length == 1 && parameters[0].isVarArgs()) {
varargConstructors.add(constructor);
}
}
if (varargConstructors.size() <= 1) return null;
final PsiType[] types = varargConstructors.stream().map(c -> c.getParameterList().getParameters()[0].getType()).toArray(PsiType[]::new);
for (int i = 1; i < types.length; i++) {
PsiType t1 = types[i];
for (int j = 0; j < i; j++) {
PsiType t2 = types[j];
if (t1.isAssignableFrom(t2)) {
varargConstructors.remove(i);
}
else if (t2.isAssignableFrom(t1)) {
varargConstructors.remove(j);
}
}
}
if (varargConstructors.size() > 1) {
final String m1 = PsiFormatUtil.formatMethod(varargConstructors.get(0), PsiSubstitutor.EMPTY,
PsiFormatUtilBase.SHOW_CONTAINING_CLASS |
PsiFormatUtilBase.SHOW_NAME |
PsiFormatUtilBase.SHOW_PARAMETERS,
PsiFormatUtilBase.SHOW_TYPE);
final String m2 = PsiFormatUtil.formatMethod(varargConstructors.get(1), PsiSubstitutor.EMPTY,
PsiFormatUtilBase.SHOW_CONTAINING_CLASS |
PsiFormatUtilBase.SHOW_NAME |
PsiFormatUtilBase.SHOW_PARAMETERS,
PsiFormatUtilBase.SHOW_TYPE);
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR)
.range(range)
.descriptionAndTooltip(JavaErrorMessages.message("ambiguous.method.call", m1, m2))
.create();
}
return null;
}
@Nullable
static HighlightInfo checkInterfaceCannotBeLocal(PsiClass aClass) {
if (PsiUtil.isLocalClass(aClass)) {
@@ -0,0 +1,28 @@
class A {
A(String... i) {}
A(Integer... i) {}
}
class B extends A {
<error descr="Ambiguous method call: both 'A.A(String...)' and 'A.A(Integer...)' match">public B()</error> {}
}
<error descr="Ambiguous method call: both 'A.A(String...)' and 'A.A(Integer...)' match">class C extends A</error> {}
class A1 {
A1(String... i){}
A1(Object... i){}
}
class B1 extends A1 {
public B1() {}
}
class C1 extends A1 {}
class A2 {
A2(int... i){}
A2(Object... i){}
}
class B2 extends A2 {
<error descr="Ambiguous method call: both 'A2.A2(int...)' and 'A2.A2(Object...)' match">public B2()</error> {
}
}
<error descr="Ambiguous method call: both 'A2.A2(int...)' and 'A2.A2(Object...)' match">class C2 extends A2</error> {}
@@ -82,4 +82,8 @@ public class LightAdvHighlightingJdk6Test extends LightDaemonAnalyzerTestCase {
public void testEnumConstantWithoutInterfaceImplementation() throws Exception {
doTest(false, false);
}
public void testAmbiguityChecksForImplicitSuperConstructorCall() {
doTest(false, false);
}
}