method references: show candidates if overload resolution failed (IDEA-148974)

This commit is contained in:
Anna Kozlova
2015-12-10 16:49:24 +01:00
parent a04d90243b
commit 3f72d428a8
28 changed files with 100 additions and 68 deletions

View File

@@ -1339,7 +1339,7 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
}
if (!myHolder.hasErrorResults()) {
if (results.length == 0) {
if (results.length == 0 || results[0] instanceof MethodCandidateInfo && !((MethodCandidateInfo)results[0]).isApplicable()) {
String description = null;
if (expression.isConstructor()) {
final PsiClass containingClass = PsiMethodReferenceUtil.getQualifierResolveResult(expression).getContainingClass();

View File

@@ -150,7 +150,7 @@ public class PsiMethodReferenceCompatibilityConstraint implements ConstraintForm
}
}
final PsiElement element = resolve.getElement();
if (element == null) {
if (element == null || resolve instanceof MethodCandidateInfo && !((MethodCandidateInfo)resolve).isApplicable()) {
session.registerIncompatibleErrorMessage("No compile-time declaration for the method reference is found");
return false;
}

View File

@@ -133,6 +133,15 @@ public class MethodReferenceResolver implements ResolveCache.PolyVariantContextR
}
return session.infer(method.getParameterList().getParameters(), null, null);
}
@Override
public boolean isApplicable() {
if (signature == null) return false;
final PsiType[] argTypes = signature.getParameterTypes();
boolean hasReceiver = PsiMethodReferenceUtil.isSecondSearchPossible(argTypes, qualifierResolveResult, reference);
return MethodReferenceConflictResolver.isApplicableByFirstSearch(this, argTypes, hasReceiver, interfaceMethod.isVarArgs()) != null;
}
};
}
};
@@ -211,8 +220,14 @@ public class MethodReferenceResolver implements ResolveCache.PolyVariantContextR
protected CandidateInfo guardedOverloadResolution(@NotNull List<CandidateInfo> conflicts) {
if (mySignature == null) return null;
if (conflicts.size() > 1) checkSameSignatures(conflicts);
if (conflicts.size() > 1) checkAccessStaticLevels(conflicts, true);
if (conflicts.isEmpty()) return null;
if (conflicts.size() == 1) return conflicts.get(0);
checkSameSignatures(conflicts);
if (conflicts.size() == 1) return conflicts.get(0);
checkAccessStaticLevels(conflicts, true);
if (conflicts.size() == 1) return conflicts.get(0);
final PsiType[] argTypes = mySignature.getParameterTypes();
boolean hasReceiver = PsiMethodReferenceUtil.isSecondSearchPossible(argTypes, myQualifierResolveResult, myReferenceExpression);
@@ -222,23 +237,9 @@ public class MethodReferenceResolver implements ResolveCache.PolyVariantContextR
for (CandidateInfo conflict : conflicts) {
if (!(conflict instanceof MethodCandidateInfo)) continue;
final PsiMethod psiMethod = ((MethodCandidateInfo)conflict).getElement();
final PsiSubstitutor substitutor = ((MethodCandidateInfo)conflict).getSubstitutor(false);
final PsiType[] parameterTypes = psiMethod.getSignature(substitutor).getParameterTypes();
final boolean varargs = ((MethodCandidateInfo)conflict).isVarargs();
if (varargs && (!psiMethod.isVarArgs() || myFunctionalMethodVarArgs)) continue;
if ((varargs || argTypes.length == parameterTypes.length) &&
PsiMethodReferenceUtil.isCorrectAssignment(parameterTypes, argTypes, varargs, 0)) {
firstCandidates.add(conflict);
}
if (hasReceiver &&
(varargs || argTypes.length == parameterTypes.length + 1) &&
PsiMethodReferenceUtil.isCorrectAssignment(parameterTypes, argTypes, varargs, 1)) {
secondCandidates.add(conflict);
final Boolean applicableByFirstSearch = isApplicableByFirstSearch(conflict, argTypes, hasReceiver, myFunctionalMethodVarArgs);
if (applicableByFirstSearch != null) {
(applicableByFirstSearch ? firstCandidates : secondCandidates).add(conflict);
}
}
@@ -264,15 +265,42 @@ public class MethodReferenceResolver implements ResolveCache.PolyVariantContextR
return !firstCandidates.isEmpty() ? firstCandidates.get(0) : secondCandidates.get(0);
}
if (firstCandidates.isEmpty() && secondCandidates.isEmpty() && conflicts.size() > 1) {
if (firstCandidates.isEmpty() && secondCandidates.isEmpty()) {
return null;
}
conflicts.clear();
firstCandidates.addAll(secondCandidates);
conflicts.addAll(firstCandidates);
return null;
}
private static Boolean isApplicableByFirstSearch(CandidateInfo conflict, PsiType[] argTypes,
boolean hasReceiver,
boolean functionalMethodVarArgs) {
final PsiMethod psiMethod = ((MethodCandidateInfo)conflict).getElement();
final PsiSubstitutor substitutor = ((MethodCandidateInfo)conflict).getSubstitutor(false);
final PsiType[] parameterTypes = psiMethod.getSignature(substitutor).getParameterTypes();
final boolean varargs = ((MethodCandidateInfo)conflict).isVarargs();
if (varargs && (!psiMethod.isVarArgs() || functionalMethodVarArgs)) {
return null;
}
if ((varargs || argTypes.length == parameterTypes.length) &&
PsiMethodReferenceUtil.isCorrectAssignment(parameterTypes, argTypes, varargs, 0)) {
return true;
}
if (hasReceiver &&
(varargs || argTypes.length == parameterTypes.length + 1) &&
PsiMethodReferenceUtil.isCorrectAssignment(parameterTypes, argTypes, varargs, 1)) {
return false;
}
return null;
}
private boolean resolveConflicts(List<CandidateInfo> firstCandidates, List<CandidateInfo> secondCandidates, int applicabilityLevel) {
final int firstApplicability = checkApplicability(firstCandidates);

View File

@@ -450,6 +450,10 @@ public class PsiMethodReferenceExpressionImpl extends PsiReferenceExpressionBase
map.remove(this);
}
if (result instanceof MethodCandidateInfo && !((MethodCandidateInfo)result).isApplicable()) {
return false;
}
final PsiElement resolve = result.getElement();
if (resolve == null) {
return false;

View File

@@ -40,6 +40,6 @@ class AAmbiguous {
}
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>;
helloWorld.then(AAmbiguous::<error descr="Cannot resolve method 'calculateLength'">calculateLength</error>);
}
}

View File

@@ -26,8 +26,8 @@ class AlienTest {
static {
IInt i1 = MyTest::<error descr="Cannot resolve method 'abracadabra'">abracadabra</error>;
IInt i2 = MyTest::<error descr="Cannot resolve method 'foo'">foo</error>;
<error descr="Incompatible types. Found: '<method reference>', required: 'AlienTest.IInt'">IInt i3 = MyTest::bar;</error>
IInt i2 = <error descr="Bad return type in method reference: cannot convert void to int">MyTest::foo</error>;
IInt i3 = MyTest::<error descr="Cannot resolve method 'bar'">bar</error>;
<error descr="Incompatible types. Found: '<method reference>', required: 'AlienTest.IIntInt'">IIntInt i4 = MyTest::bar;</error>
IInt i5 = <error descr="Non-static method cannot be referenced from a static context">MyTest::baz</error>;
IInt i6 = <error descr="'foo(int)' is not public in 'MyTest.Foo'. Cannot be accessed from outside package">MyTest.foo::foo</error>;

View File

@@ -29,7 +29,7 @@ class MyTest {
{
Bar1 b1 = MyTest :: foo;
bar<error descr="Ambiguous method call: both 'MyTest.bar(Bar1)' and 'MyTest.bar(Bar2)' match">(MyTest :: foo)</error>;
bar(MyTest :: <error descr="Cannot resolve method 'foo'">foo</error>);
}
}
@@ -129,7 +129,7 @@ class MyTest4 {
}
{
bar<error descr="'bar(todelete.MyTest4.Bar1)' in 'todelete.MyTest4' cannot be applied to '(<method reference>)'">(MyTest4:: foo)</error>;
bar(MyTest4:: <error descr="Cannot resolve method 'foo'">foo</error>);
}
}

View File

@@ -16,7 +16,7 @@ class Test {
class Test1 {
{
<error descr="Incompatible types. Found: '<method reference>', required: 'java.lang.Runnable'">Runnable b = Test1 :: length;</error>
Runnable b = Test1 :: <error descr="Cannot resolve method 'length'">length</error>;
Comparable<String> c = Test1 :: length;
Comparable<Integer> c1 = Test1 :: length;
}

View File

@@ -70,8 +70,8 @@ class MyTest3<X> {
static void test(I<Integer> s) { }
public static void main(String[] args) {
I<Integer> s = MyTest3<String>::<error descr="Cannot resolve constructor 'MyTest3'">new</error>;
test(MyTest3<String>::<error descr="Cannot resolve constructor 'MyTest3'">new</error>);
I<Integer> s = <error descr="Bad return type in method reference: cannot convert MyTest3<java.lang.String> to MyTest3<java.lang.Integer>">MyTest3<String>::new</error>;
test(<error descr="Bad return type in method reference: cannot convert MyTest3<java.lang.String> to MyTest3<java.lang.Integer>">MyTest3<String>::new</error>);
}
}

View File

@@ -71,7 +71,7 @@ class StaticInner1 {
static void call3(I2 s) {}
static {
call3<error descr="Ambiguous method call: both 'StaticInner1.call3(I1)' and 'StaticInner1.call3(I2)' match">(StaticInner1.Inner :: new)</error>;
call3(StaticInner1.Inner :: <error descr="Cannot resolve constructor 'Inner'">new</error>);
}
}
@@ -109,7 +109,7 @@ class NonStaticInner2 {
static {
I1 i1 = NonStaticInner2.Inner :: <error descr="Cannot resolve constructor 'Inner'">new</error>;
I1 i1 = <error descr="An enclosing instance of type NonStaticInner2 is not in scope">NonStaticInner2.Inner :: new</error>;
}
{
@@ -145,7 +145,7 @@ class NonStaticInner3 {
}
{
<error descr="Incompatible types. Found: '<method reference>', required: 'NonStaticInner3.I3<NonStaticInner3.Foo>'">I3<Foo> b1 = Foo::new;</error>
<error descr="Incompatible types. Found: '<method reference>', required: 'NonStaticInner3.I4<NonStaticInner3.Foo>'">I4<Foo> b2 = Foo::new;</error>
I3<Foo> b1 = Foo::<error descr="Cannot resolve constructor 'Foo'">new</error>;
I4<Foo> b2 = Foo::<error descr="Cannot resolve constructor 'Foo'">new</error>;
}
}

View File

@@ -49,7 +49,7 @@ class MyTestConstructor {
private static void <warning descr="Private method 'foo(MyTestConstructor.I3)' is never used">foo</warning>(I3 i) {System.out.println(i);}
static {
foo<error descr="Ambiguous method call: both 'MyTestConstructor.foo(I1)' and 'MyTestConstructor.foo(I2)' match">(Foo::new)</error>;
foo(Foo::<error descr="Cannot resolve constructor 'Foo'">new</error>);
}
}
@@ -78,6 +78,6 @@ class MyTestMethod {
private static void <warning descr="Private method 'foo(MyTestMethod.I3)' is never used">foo</warning>(I3 i) {System.out.println(i);}
static {
foo<error descr="Ambiguous method call: both 'MyTestMethod.foo(I1)' and 'MyTestMethod.foo(I2)' match">(MyTestMethod::m)</error>;
foo(MyTestMethod::<error descr="Cannot resolve method 'm'">m</error>);
}
}

View File

@@ -38,7 +38,7 @@ class MyTest1 {
}
public static void main(String[] args) {
call<error descr="Ambiguous method call: both 'MyTest1.call(int, I1)' and 'MyTest1.call(int, I2)' match">(1, MyTest1::m)</error>;
call(1, MyTest1::<error descr="Cannot resolve method 'm'">m</error>);
}
}
@@ -192,6 +192,6 @@ class MyTest9 {
void test() {
g1(MyTest9::m);
g2<error descr="Ambiguous method call: both 'MyTest9.g2(I1)' and 'MyTest9.g2(I2)' match">(MyTest9::m)</error>;
g2(MyTest9::<error descr="Cannot resolve method 'm'">m</error>);
}
}

View File

@@ -20,8 +20,8 @@ class MyTest {
static void foo(I3 i) {}
static {
foo<error descr="Ambiguous method call: both 'MyTest.foo(I1)' and 'MyTest.foo(I2)' match">(MyTest::m)</error>;
foo<error descr="Ambiguous method call: both 'MyTest.foo(I1)' and 'MyTest.foo(I2)' match">(MyTest::m1)</error>;
foo(MyTest::<error descr="Cannot resolve method 'm'">m</error>);
foo(MyTest::<error descr="Cannot resolve method 'm1'">m1</error>);
}
}
@@ -44,9 +44,9 @@ class MyTest1 {
static {
foo1(MyTest1::m);
foo2<error descr="Ambiguous method call: both 'MyTest1.foo2(I1)' and 'MyTest1.foo2(I2)' match">(MyTest1::m)</error>;
foo2(MyTest1::<error descr="Cannot resolve method 'm'">m</error>);
foo1(MyTest1::m1);
foo2<error descr="Ambiguous method call: both 'MyTest1.foo2(I1)' and 'MyTest1.foo2(I2)' match">(MyTest1::m1)</error>;
foo2(MyTest1::<error descr="Cannot resolve method 'm1'">m1</error>);
}
}

View File

@@ -12,7 +12,7 @@ class MyTest {
{
I i = Foo<String> :: foo;
I i1 = Foo<Integer> :: <error descr="Cannot resolve method 'foo'">foo</error>;
I i1 = <error descr="Non-static method cannot be referenced from a static context">Foo<Integer> :: foo</error>;
}
}
}

View File

@@ -39,6 +39,6 @@ class MyTest1 {
static void foo(I3 i) {}
static {
foo<error descr="Ambiguous method call: both 'MyTest1.foo(I1)' and 'MyTest1.foo(I2)' match">(Foo::new)</error>;
foo(Foo::<error descr="Cannot resolve constructor 'Foo'">new</error>);
}
}

View File

@@ -29,7 +29,7 @@ class MyTest {
}
public static void main(String[] args) {
foo<error descr="Ambiguous method call: both 'MyTest.foo(I1)' and 'MyTest.foo(I2)' match">(Foo::m)</error>;
foo(Foo::<error descr="Cannot resolve method 'm'">m</error>);
}
}
@@ -67,7 +67,7 @@ class MyTest1 {
}
public static void main(String[] args) {
m<error descr="Ambiguous method call: both 'MyTest1.m(I1)' and 'MyTest1.m(I2)' match">(Foo::new)</error>;
m(Foo::<error descr="Cannot resolve constructor 'Foo'">new</error>);
}
}
class MyTest2 {
@@ -104,6 +104,6 @@ class MyTest2 {
}
public static void main(String[] args) {
m<error descr="Ambiguous method call: both 'MyTest2.m(I1)' and 'MyTest2.m(I2)' match">(Foo::new)</error>;
m(Foo::<error descr="Cannot resolve constructor 'Foo'">new</error>);
}
}

View File

@@ -2,7 +2,7 @@ import java.util.*;
class LambdaTest {
public void testR() {
<error descr="Incompatible types. Found: 'java.lang.String', required: '<method reference>'">new ArrayList<String>() :: size = ""</error>;
new ArrayList<String>() :: <error descr="Cannot resolve method 'size'">size</error> = "";
}
}

View File

@@ -116,7 +116,7 @@ class MyTest2 {
static void call3(I2 s) {}
static {
call3(MyTest2::m1);
call3<error descr="Ambiguous method call: both 'MyTest2.call3(I1)' and 'MyTest2.call3(I2)' match">(MyTest2::m2)</error>;
call3(MyTest2::<error descr="Cannot resolve method 'm2'">m2</error>);
call3(MyTest2::m3);
call3<error descr="'call3(MyTest2.I2)' in 'MyTest2' cannot be applied to '(<method reference>)'">(MyTest2::m4)</error>;
}

View File

@@ -1,6 +1,6 @@
public class Test {
{
if (<error descr="Method reference expression is not expected here">Test::length instanceof String</error>) {
if (Test::<error descr="Cannot resolve method 'length'">length</error> instanceof String) {
}
bar(Test::length);
}

View File

@@ -50,20 +50,20 @@ class MyTest {
I1 i1 = MyTest::static_1;
I1 i2 = MyTest::<error descr="Cannot resolve method 'static_2'">static_2</error>;
I1 i3 = MyTest::<error descr="Cannot resolve method 'static_3'">static_3</error>;
<error descr="Incompatible types. Found: '<method reference>', required: 'MyTest.I1'">I1 i4 = MyTest::static_4;</error>
I1 i4 = MyTest::<error descr="Cannot resolve method 'static_4'">static_4</error>;
}
{
I1 i_1 = <error descr="Non-static method cannot be referenced from a static context">MyTest::_1</error>;
I1 i_2 = MyTest::<error descr="Cannot resolve method '_2'">_2</error>;
I1 i_3 = MyTest::<error descr="Cannot resolve method '_3'">_3</error>;
<error descr="Incompatible types. Found: '<method reference>', required: 'MyTest.I1'">I1 i_4 = MyTest::_4;</error>
I1 i_2 = <error descr="Non-static method cannot be referenced from a static context">MyTest::_2</error>;
I1 i_3 = <error descr="Non-static method cannot be referenced from a static context">MyTest::_3</error>;
I1 i_4 = MyTest::<error descr="Cannot resolve method '_4'">_4</error>;
I1 i1 = this::_1;
I1 i2 = this::<error descr="Cannot resolve method '_2'">_2</error>;
I1 i3 = this::<error descr="Cannot resolve method '_3'">_3</error>;
<error descr="Incompatible types. Found: '<method reference>', required: 'MyTest.I1'">I1 i4 = this::_4;</error>
I1 i4 = this::<error descr="Cannot resolve method '_4'">_4</error>;
I2 i21 = MyTest::<error descr="Cannot resolve method 'm1'">m1</error>;
I2 i22 = MyTest::<error descr="Cannot resolve method 'm2'">m2</error>;

View File

@@ -4,7 +4,7 @@ class Test {
void test() {
Comparator<Test> r2 = Test::yyy;
Comparator1<Test> c1 = <error descr="Non-static method cannot be referenced from a static context">Test::yyy</error>;
Comparator1<Test> c2 = Test::<error descr="Cannot resolve method 'xxx'">xxx</error>;
Comparator1<Test> c2 = <error descr="Non-static method cannot be referenced from a static context">Test::xxx</error>;
}
int yyy(Test... p) { return 1; }
int xxx(Test t) {return 42;}

View File

@@ -28,6 +28,6 @@ class Test2 {
static void call(I2 i2) { System.out.println(i2); }
static {
call<error descr="Ambiguous method call: both 'Test2.call(I1)' and 'Test2.call(I2)' match">(Test2::m)</error>;
call(Test2::<error descr="Cannot resolve method 'm'">m</error>);
}
}

View File

@@ -12,7 +12,7 @@ class Test {
void m(LongMapper... lm) { }
{
m<error descr="Ambiguous method call: both 'Test.m(IntMapper, IntMapper...)' and 'Test.m(LongMapper...)' match">(this ::ii)</error>;
m(this ::<error descr="Cannot resolve method 'ii'">ii</error>);
}
int ii() {return 0;}

View File

@@ -32,12 +32,12 @@ class Test {
meth1(Foo::<error descr="Cannot resolve constructor 'Foo'">new</error>);
meth2(Foo::new);
meth3(Foo::<error descr="Cannot resolve constructor 'Foo'">new</error>);
meth4<error descr="Ambiguous method call: both 'Test.meth4(I1)' and 'Test.meth4(I2)' match">(Foo::new)</error>;
meth4(Foo::<error descr="Cannot resolve constructor 'Foo'">new</error>);
meth1(Test::<error descr="Cannot resolve method 'foo'">foo</error>);
meth2(Test::foo);
meth3(Test::<error descr="Cannot resolve method 'foo'">foo</error>);
meth4<error descr="Ambiguous method call: both 'Test.meth4(I1)' and 'Test.meth4(I2)' match">(Test::foo)</error>;
meth4(Test::<error descr="Cannot resolve method 'foo'">foo</error>);
}

View File

@@ -31,8 +31,8 @@ class Test {
String i1 = instanceCall(this::m0);
String i2 = instanceCall(this::m1);
String i3 = instanceCall(this::m2);
String i4 = instanceCall<error descr="Ambiguous method call: both 'Test.instanceCall(I0)' and 'Test.instanceCall(I1<String>)' match">(this::m01)</error>;
String i5 = instanceCall<error descr="Ambiguous method call: both 'Test.instanceCall(I0)' and 'Test.instanceCall(I1<String>)' match">(this::m012)</error>;
String i4 = instanceCall(this::<error descr="Cannot resolve method 'm01'">m01</error>);
String i5 = instanceCall(this::<error descr="Cannot resolve method 'm012'">m012</error>);
}
void n0() { }
@@ -52,8 +52,8 @@ class Test {
static {
Test s1 = staticCall(Test::n0);
Test s2 = staticCall(Test::n1);
Test s3 = <error descr="Cannot resolve method 'staticCall(<method reference>)'">staticCall</error>(Test::n2);
Test s4 = staticCall<error descr="Ambiguous method call: both 'Test.staticCall(I1<Test>)' and 'Test.staticCall(I2<Test, String>)' match">(Test::n01)</error>;
Test s5 = staticCall<error descr="Ambiguous method call: both 'Test.staticCall(I1<Test>)' and 'Test.staticCall(I2<Test, String>)' match">(Test::n012)</error>;
Test s3 = <error descr="Cannot resolve method 'staticCall(<method reference>)'">staticCall</error>(Test::<error descr="Cannot resolve method 'n2'">n2</error>);
Test s4 = staticCall(Test::<error descr="Cannot resolve method 'n01'">n01</error>);
Test s5 = staticCall(Test::<error descr="Cannot resolve method 'n012'">n012</error>);
}
}

View File

@@ -34,7 +34,7 @@ class Test {
{
Set<String> m = replyWith(this::query);
System.out.println(m);
Set<String> m1 = replyWith<error descr="Ambiguous method call: both 'Test.replyWith(Function<String, List<String>>)' and 'Test.replyWith(Callable<List<String>>)' match">(this::query1)</error>;
Set<String> m1 = replyWith(this::<error descr="Cannot resolve method 'query1'">query1</error>);
System.out.println(m1);
}
}

View File

@@ -4,6 +4,6 @@ import java.util.Comparator;
class Main {
public void test() {
Collections.sort(new ArrayList<>(), Comparator::<error descr="Cannot resolve method 'reversed'">reversed</error>);
Collections.sort(new ArrayList<>(), <error descr="Non-static method cannot be referenced from a static context">Comparator::reversed</error>);
}
}

View File

@@ -19,7 +19,7 @@ class Test {
private void <warning descr="Private method 'm1(I3)' is never used">m1</warning>(I3 i2) {System.out.println(i2);}
void test() {
m <error descr="Ambiguous method call: both 'Test.m(I1)' and 'Test.m(I2)' match">(this::bar)</error>;
m (this::<error descr="Cannot resolve method 'bar'">bar</error>);
m1(this::bar);
}