lambda conflict resolve: check lambdas on per-params basis

This commit is contained in:
anna
2012-07-19 14:26:09 +02:00
parent 549da5f6df
commit d35061a610
5 changed files with 119 additions and 6 deletions

View File

@@ -70,4 +70,8 @@ public class PsiLambdaExpressionType extends PsiType {
public PsiType[] getSuperTypes() {
return PsiType.EMPTY_ARRAY;
}
public PsiLambdaExpression getExpression() {
return myExpression;
}
}

View File

@@ -99,6 +99,7 @@ public class LambdaUtil {
@Nullable
public static MethodSignature getFunction(PsiClass psiClass) {
if (psiClass == null) return null;
final List<MethodSignature> functions = findFunctionCandidates(psiClass);
if (functions != null && functions.size() == 1) {
return functions.get(0);
@@ -123,12 +124,9 @@ public class LambdaUtil {
}
if (type instanceof PsiClassType) {
final PsiClassType.ClassResolveResult resolveResult = ((PsiClassType)type).resolveGenerics();
final PsiClass psiClass = resolveResult.getElement();
if (psiClass != null) {
final MethodSignature methodSignature = getFunction(psiClass);
if (methodSignature != null) {
return resolveResult.getSubstitutor().substitute(methodSignature.getParameterTypes()[parameterIndex]);
}
final MethodSignature methodSignature = getFunction(resolveResult.getElement());
if (methodSignature != null) {
return resolveResult.getSubstitutor().substitute(methodSignature.getParameterTypes()[parameterIndex]);
}
}
}

View File

@@ -22,6 +22,7 @@ import com.intellij.openapi.util.Comparing;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.*;
import com.intellij.psi.impl.PsiSuperMethodImplUtil;
import com.intellij.psi.impl.source.tree.java.LambdaUtil;
import com.intellij.psi.infos.CandidateInfo;
import com.intellij.psi.infos.MethodCandidateInfo;
import com.intellij.psi.scope.PsiConflictResolver;
@@ -86,11 +87,49 @@ public class JavaMethodsConflictResolver implements PsiConflictResolver{
checkPrimitiveVarargs(conflicts, myActualParameterTypes.length);
if (conflicts.size() == 1) return conflicts.get(0);
checkLambdaApplicable(conflicts);
if (conflicts.size() == 1) return conflicts.get(0);
THashSet<CandidateInfo> uniques = new THashSet<CandidateInfo>(conflicts);
if (uniques.size() == 1) return uniques.iterator().next();
return null;
}
private void checkLambdaApplicable(List<CandidateInfo> conflicts) {
for (int i = 0; i < myActualParameterTypes.length; i++) {
PsiType parameterType = myActualParameterTypes[i];
if (parameterType instanceof PsiLambdaExpressionType) {
final PsiLambdaExpression lambdaExpression = ((PsiLambdaExpressionType)parameterType).getExpression();
final int parametersCount = lambdaExpression.getParameterList().getParametersCount();
for (Iterator<CandidateInfo> iterator = conflicts.iterator(); iterator.hasNext(); ) {
final CandidateInfo conflict = iterator.next();
final PsiMethod method = (PsiMethod)conflict.getElement();
if (method != null) {
final PsiParameter[] methodParameters = method.getParameterList().getParameters();
final PsiParameter param = i < methodParameters.length ? methodParameters[i] : methodParameters[methodParameters.length - 1];
final PsiClassType.ClassResolveResult resolveResult = PsiUtil.resolveGenericsClassInType(param.getType());
final MethodSignature function = LambdaUtil.getFunction(resolveResult.getElement());
if (function != null && function.getParameterTypes().length == parametersCount) {
boolean correctArgs = true;
final PsiParameter[] lambdaParameters = lambdaExpression.getParameterList().getParameters();
for (int lambdaParamIdx = 0, length = lambdaParameters.length; lambdaParamIdx < length; lambdaParamIdx++) {
PsiParameter parameter = lambdaParameters[lambdaParamIdx];
final PsiTypeElement typeElement = parameter.getTypeElement();
if (typeElement != null) {
if (!typeElement.getType().equals(resolveResult.getSubstitutor().substitute(function.getParameterTypes()[lambdaParamIdx]))) {
correctArgs = false;
}
}
}
if (correctArgs) continue;
}
iterator.remove();
}
}
}
}
}
private void checkSpecifics(List<CandidateInfo> conflicts, @MethodCandidateInfo.ApplicabilityLevelConstant int applicabilityLevel) {
final boolean applicable = applicabilityLevel > MethodCandidateInfo.ApplicabilityLevel.NOT_APPLICABLE;

View File

@@ -0,0 +1,68 @@
interface I {
void m(int i);
}
interface J {
void mm(int i, int j);
}
interface K {
void k(String m);
}
class Foo {
void foo(I i){}
void foo(J j){}
void foo(K k){}
void bar() {
foo<error descr="Ambiguous method call: both 'Foo.foo(I)' and 'Foo.foo(K)' match">((p) -> {
System.out.println(p);
})</error>;
foo((p, k) -> {
System.out.println(p);
});
foo((String s) ->{
System.out.println(s);
});
<error descr="Cannot resolve method 'foo(<lambda expression>)'">foo</error>((String p, String k) -> {
System.out.println(p);
});
}
}
class WithTypeParams {
interface I<T> {
void m(T t);
}
interface J<K, V> {
void n(K k, V v);
}
class Foo {
void foo(I<String> i){}
void foo(J<String, String> j){}
void bar() {
foo((p) -> {
System.out.println(p);
});
foo((p, k) -> {
System.out.println(p);
});
foo((String s) ->{
System.out.println(s);
});
foo((String p, String k) -> {
System.out.println(p);
});
<error descr="Cannot resolve method 'foo(<lambda expression>)'">foo</error>((int k) -> {System.out.println(k);});
}
}
}

View File

@@ -28,6 +28,10 @@ public class LambdaParamsTest extends LightDaemonAnalyzerTestCase {
doTest();
}
public void testMethodApplicability() throws Exception {
doTest();
}
private void doTest() throws Exception {
doTest(BASE_PATH + "/" + getTestName(false) + ".java", false, false);
}