inference: simplify bounds check; ensure fresh variables are treated as same, important e.g. in case of diamond operator when inferred types are new each time they are computed (IDEA-131066)

(cherry picked from commit 517c299e336e38bc20d3478191279be2176fbd9b)
This commit is contained in:
Anna Kozlova
2014-10-13 19:33:33 +02:00
parent b2a07e2eee
commit b2ea4c6e79
7 changed files with 54 additions and 23 deletions
@@ -21,10 +21,7 @@ import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.Couple;
import com.intellij.openapi.util.Pair;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.util.InheritanceUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.psi.util.TypeConversionUtil;
import com.intellij.psi.util.TypesDistinctProver;
import com.intellij.psi.util.*;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.HashMap;
import org.jetbrains.annotations.NotNull;
@@ -505,12 +502,12 @@ public class GenericsUtil {
public static boolean checkNotInBounds(PsiType type, PsiType bound, PsiReferenceParameterList referenceParameterList) {
//allow unchecked conversions in method calls but not in type declaration
return checkNotInBounds(type, bound, referenceParameterList.getParent() instanceof PsiReferenceExpression);
return checkNotInBounds(type, bound, PsiTreeUtil.getParentOfType(referenceParameterList, PsiCallExpression.class) != null);
}
public static boolean checkNotInBounds(PsiType type, PsiType bound, boolean uncheckedConversionByDefault) {
if (type instanceof PsiClassType) {
return checkNotAssignable(bound, type, allowUncheckedConversions((PsiClassType)type, uncheckedConversionByDefault));
return checkNotAssignable(bound, type, uncheckedConversionByDefault);
}
if (type instanceof PsiWildcardType) {
if (((PsiWildcardType)type).isExtends()) {
@@ -581,18 +578,4 @@ public class GenericsUtil {
return !TypeConversionUtil.isAssignable(bound, type, allowUncheckedConversion);
}
}
private static boolean allowUncheckedConversions(PsiClassType type, boolean uncheckedConversionByDefault) {
final PsiClass psiClass = type.resolve();
if (psiClass != null) {
for (PsiTypeParameter parameter : PsiUtil.typeParametersIterable(psiClass)) {
if (parameter.getExtendsListTypes().length != 0) {
return false;
}
}
if (psiClass instanceof PsiTypeParameter && psiClass.getExtendsListTypes().length != 0) return false;
}
if (!type.isRaw()) return true;
return uncheckedConversionByDefault;
}
}
@@ -26,6 +26,7 @@ import com.intellij.psi.*;
import com.intellij.psi.filters.OrFilter;
import com.intellij.psi.impl.source.ClassInnerStuffCache;
import com.intellij.psi.impl.source.PsiImmediateClassType;
import com.intellij.psi.impl.source.resolve.graphInference.InferenceSession;
import com.intellij.psi.infos.MethodCandidateInfo;
import com.intellij.psi.scope.ElementClassFilter;
import com.intellij.psi.scope.ElementClassHint;
@@ -1005,7 +1006,7 @@ public class PsiClassImplUtil {
PsiTypeParameter p2 = (PsiTypeParameter)another;
return p1.getIndex() == p2.getIndex() &&
aClass.getManager().areElementsEquivalent(p1.getOwner(), p2.getOwner());
(aClass.getManager().areElementsEquivalent(p1.getOwner(), p2.getOwner()) || InferenceSession.areSameFreshVariables(p1, p2));
}
else {
return false;
@@ -42,7 +42,7 @@ import java.util.*;
public class InferenceSession {
private static final Logger LOG = Logger.getInstance("#" + InferenceSession.class.getName());
public static final Key<PsiType> LOWER_BOUND = Key.create("LowBound");
private static final Key<PsiElement> ORIGINAL_CONTEXT = Key.create("ORIGINAL_CONTEXT");
private static final Key<Boolean> ERASED = Key.create("UNCHECKED_CONVERSION");
private static final Function<Pair<PsiType, PsiType>, PsiType> UPPER_BOUND_FUNCTION = new Function<Pair<PsiType, PsiType>, PsiType>() {
@Override
@@ -822,7 +822,11 @@ public class InferenceSession {
LOG.assertTrue(file instanceof PsiJavaFile, classText);
final PsiClass[] classes = ((PsiJavaFile)file).getClasses();
LOG.assertTrue(classes.length == 1, classText);
return classes[0].getTypeParameters();
final PsiTypeParameter[] parameters = classes[0].getTypeParameters();
for (PsiTypeParameter parameter : parameters) {
parameter.putUserData(ORIGINAL_CONTEXT, myContext);
}
return parameters;
}
private static String getFreshVariableName(InferenceVariable var) {
@@ -1394,4 +1398,9 @@ public class InferenceSession {
}
return s.substitute(type);
}
public static boolean areSameFreshVariables(PsiTypeParameter p1, PsiTypeParameter p2) {
final PsiElement originalContext = p1.getUserData(ORIGINAL_CONTEXT);
return originalContext != null && originalContext == p2.getUserData(ORIGINAL_CONTEXT);
}
}
@@ -0,0 +1,15 @@
class Builders {
public static A foo() {
return new A.Builder<>().create();
}
static class A<K extends A.Builder<K>> {
public static class Builder<T extends Builder<T>> {
public A<T> create() {
return null;
}
}
}
}
@@ -0,0 +1,15 @@
class Builders {
public static A foo() {
return new A.Builder<>().create();
}
static class A<K extends A.Builder<K>> {
public static class Builder<T extends Builder<T>> {
public A<T> create() {
return null;
}
}
}
}
@@ -384,6 +384,10 @@ public class GenericsHighlightingTest extends LightDaemonAnalyzerTestCase {
doTest(LanguageLevel.JDK_1_7, JavaSdkVersion.JDK_1_7, false);
}
public void testInferredParameterInBoundsInRecursiveGenerics() {
doTest(LanguageLevel.JDK_1_7, JavaSdkVersion.JDK_1_7, false);
}
public void testHiddenMethodsOfAnonymousClass() throws Exception {
doTest(LanguageLevel.JDK_1_7, JavaSdkVersion.JDK_1_7, false);
}
@@ -766,6 +766,10 @@ public class GenericsHighlighting8Test extends LightDaemonAnalyzerTestCase {
doTest(true);
}
public void testInferredParameterInBoundsInRecursiveGenerics() {
doTest(false);
}
private void doTest() {
doTest(false);
}