mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-04-20 21:41:24 +07:00
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:
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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>;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -228,6 +228,10 @@ public class OverloadResolutionTest extends LightDaemonAnalyzerTestCase {
|
||||
doTest(false);
|
||||
}
|
||||
|
||||
public void testIgnoreLambdaVoidValueIncompatibilitiesPreferringMethodWithFunctionalTypeToNonFunctionalType() throws Exception {
|
||||
doTest(false);
|
||||
}
|
||||
|
||||
private void doTest() {
|
||||
doTest(true);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user