overload resolution: prefer candidates with functional formal type if lambda is passed; this way conflict would be resolved and nested error would be shown instead

This commit is contained in:
Anna Kozlova
2016-09-16 08:32:43 +03:00
parent a493ff0f1d
commit f4dae32d7f
5 changed files with 82 additions and 23 deletions

View File

@@ -27,6 +27,7 @@ import com.intellij.psi.impl.source.resolve.DefaultParameterTypeInferencePolicy;
import com.intellij.psi.impl.source.resolve.ParameterTypeInferencePolicy;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.ThreeState;
import com.intellij.util.containers.ContainerUtil;
import org.intellij.lang.annotations.MagicConstant;
import org.jetbrains.annotations.NotNull;
@@ -132,7 +133,7 @@ public class MethodCandidateInfo extends CandidateInfo{
}
//already performed checks, so if inference failed, error message should be saved
if (myInferenceError != null || !isPotentiallyCompatible()) {
if (myInferenceError != null || isPotentiallyCompatible() != ThreeState.YES) {
return ApplicabilityLevel.NOT_APPLICABLE;
}
return isVarargs() ? ApplicabilityLevel.VARARGS : ApplicabilityLevel.FIXED_ARITY;
@@ -176,7 +177,7 @@ public class MethodCandidateInfo extends CandidateInfo{
/**
* 15.12.2.1 Identify Potentially Applicable Methods
*/
public boolean isPotentiallyCompatible() {
public ThreeState isPotentiallyCompatible() {
if (myArgumentList instanceof PsiExpressionList) {
final PsiMethod method = getElement();
final PsiParameter[] parameters = method.getParameterList().getParameters();
@@ -184,19 +185,21 @@ public class MethodCandidateInfo extends CandidateInfo{
if (!isVarargs() && myLanguageLevel.isAtLeast(LanguageLevel.JDK_1_8)) {
if (expressions.length != parameters.length) {
return false;
return ThreeState.NO;
}
}
else {
if (expressions.length < parameters.length - 1) {
return false;
return ThreeState.NO;
}
if (parameters.length == 0 && expressions.length != parameters.length) {
return false;
return ThreeState.NO;
}
}
boolean unsure = false;
for (int i = 0; i < expressions.length; i++) {
final PsiExpression expression = expressions[i];
PsiType formalParameterType = i < parameters.length ? parameters[i].getType() : parameters[parameters.length - 1].getType();
@@ -205,35 +208,54 @@ public class MethodCandidateInfo extends CandidateInfo{
formalParameterType = ((PsiEllipsisType)formalParameterType).getComponentType();
}
if (!isPotentialCompatible(expression, getSiteSubstitutor().substitute(formalParameterType), method)) {
return false;
ThreeState compatible = isPotentialCompatible(expression, getSiteSubstitutor().substitute(formalParameterType), method);
if (compatible == ThreeState.NO) {
return ThreeState.NO;
}
if (compatible == ThreeState.UNSURE) {
unsure = true;
}
}
if (unsure) return ThreeState.UNSURE;
if (method.hasTypeParameters() && myTypeArguments != null) {
return method.getTypeParameters().length == myTypeArguments.length; //todo
return ThreeState.fromBoolean(method.getTypeParameters().length == myTypeArguments.length); //todo
}
}
return true;
return ThreeState.YES;
}
private static boolean isPotentialCompatible(PsiExpression expression, PsiType formalType, PsiMethod method) {
private static ThreeState isPotentialCompatible(PsiExpression expression, PsiType formalType, PsiMethod method) {
if (expression instanceof PsiFunctionalExpression) {
final PsiClass targetTypeParameter = PsiUtil.resolveClassInClassTypeOnly(formalType);
if (targetTypeParameter instanceof PsiTypeParameter && method.equals(((PsiTypeParameter)targetTypeParameter).getOwner())) {
return true;
return ThreeState.YES;
}
if (!LambdaUtil.isFunctionalType(formalType)) {
return ThreeState.NO;
}
if (!((PsiFunctionalExpression)expression).isPotentiallyCompatible(formalType)) {
return false;
return ThreeState.UNSURE;
}
}
else if (expression instanceof PsiParenthesizedExpression) {
return isPotentialCompatible(((PsiParenthesizedExpression)expression).getExpression(), formalType, method);
}
else if (expression instanceof PsiConditionalExpression) {
return isPotentialCompatible(((PsiConditionalExpression)expression).getThenExpression(), formalType, method) &&
isPotentialCompatible(((PsiConditionalExpression)expression).getElseExpression(), formalType, method);
ThreeState thenCompatible = isPotentialCompatible(((PsiConditionalExpression)expression).getThenExpression(), formalType, method);
ThreeState elseCompatible = isPotentialCompatible(((PsiConditionalExpression)expression).getElseExpression(), formalType, method);
if (thenCompatible == ThreeState.NO || elseCompatible == ThreeState.NO) {
return ThreeState.NO;
}
if (thenCompatible == ThreeState.UNSURE || elseCompatible == ThreeState.UNSURE) {
return ThreeState.UNSURE;
}
}
return true;
return ThreeState.YES;
}
private <T> T computeForOverloadedCandidate(final Computable<T> computable,

View File

@@ -32,6 +32,7 @@ import com.intellij.psi.infos.MethodCandidateInfo;
import com.intellij.psi.scope.PsiConflictResolver;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.util.*;
import com.intellij.util.ThreeState;
import com.intellij.util.containers.FactoryMap;
import com.intellij.util.containers.HashSet;
import gnu.trove.THashMap;
@@ -40,10 +41,7 @@ import gnu.trove.TIntArrayList;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.*;
/**
* Created by IntelliJ IDEA.
@@ -140,13 +138,23 @@ public class JavaMethodsConflictResolver implements PsiConflictResolver{
}
private static void checkPotentiallyCompatibleMethods(@NotNull List<CandidateInfo> conflicts) {
List<CandidateInfo> partiallyApplicable = new ArrayList<CandidateInfo>();
for (Iterator<CandidateInfo> iterator = conflicts.iterator(); iterator.hasNext(); ) {
CandidateInfo conflict = iterator.next();
if (conflict instanceof MethodCandidateInfo &&
!((MethodCandidateInfo)conflict).isPotentiallyCompatible()) {
iterator.remove();
if (conflict instanceof MethodCandidateInfo) {
ThreeState compatible = ((MethodCandidateInfo)conflict).isPotentiallyCompatible();
if (compatible == ThreeState.NO) {
iterator.remove();
}
else if (compatible == ThreeState.UNSURE) {
partiallyApplicable.add(conflict);
}
}
}
if (conflicts.size() > partiallyApplicable.size()) {
conflicts.removeAll(partiallyApplicable);
}
}
public void checkSpecifics(@NotNull List<CandidateInfo> conflicts,

View File

@@ -52,7 +52,7 @@ 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 s3 = staticCall<error descr="Cannot resolve method 'staticCall(<method reference>)'">(Test::n2)</error>;
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>;
}

View File

@@ -0,0 +1,25 @@
import java.util.function.*;
class Test {
void foo(Function<String, String> f) {}
void foo(String f) {}
{
foo(a -> {
String s = a.substring(0);
<error descr="Missing return statement">}</error>);
}
interface A {
void m(String s);
}
void bar(Function<String, String> f) {}
void bar(A f) {}
{
bar(a -> {
String s = a.substring(0);
});
}
}

View File

@@ -228,6 +228,10 @@ public class OverloadResolutionTest extends LightDaemonAnalyzerTestCase {
doTest(false);
}
public void testIgnoreLambdaVoidValueIncompatibilitiesPreferringMethodWithFunctionalTypeToNonFunctionalType() throws Exception {
doTest(false);
}
private void doTest() {
doTest(true);
}