diff --git a/java/java-psi-api/src/com/intellij/psi/GenericsUtil.java b/java/java-psi-api/src/com/intellij/psi/GenericsUtil.java index 4646aea635b3..0818ea4e41a0 100644 --- a/java/java-psi-api/src/com/intellij/psi/GenericsUtil.java +++ b/java/java-psi-api/src/com/intellij/psi/GenericsUtil.java @@ -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; - } } diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/PsiClassImplUtil.java b/java/java-psi-impl/src/com/intellij/psi/impl/PsiClassImplUtil.java index 630ff802796a..7e12adac007d 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/PsiClassImplUtil.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/PsiClassImplUtil.java @@ -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; diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceSession.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceSession.java index 542d5b7e7299..51350e124dd3 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceSession.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceSession.java @@ -42,7 +42,7 @@ import java.util.*; public class InferenceSession { private static final Logger LOG = Logger.getInstance("#" + InferenceSession.class.getName()); public static final Key LOWER_BOUND = Key.create("LowBound"); - + private static final Key ORIGINAL_CONTEXT = Key.create("ORIGINAL_CONTEXT"); private static final Key ERASED = Key.create("UNCHECKED_CONVERSION"); private static final Function, PsiType> UPPER_BOUND_FUNCTION = new Function, 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); + } } diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting/InferredParameterInBoundsInRecursiveGenerics.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting/InferredParameterInBoundsInRecursiveGenerics.java new file mode 100644 index 000000000000..05ece82053e1 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting/InferredParameterInBoundsInRecursiveGenerics.java @@ -0,0 +1,15 @@ +class Builders { + public static A foo() { + return new A.Builder<>().create(); + } + + static class A> { + public static class Builder> { + public A create() { + return null; + } + } + } +} + + diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting8/InferredParameterInBoundsInRecursiveGenerics.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting8/InferredParameterInBoundsInRecursiveGenerics.java new file mode 100644 index 000000000000..05ece82053e1 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting8/InferredParameterInBoundsInRecursiveGenerics.java @@ -0,0 +1,15 @@ +class Builders { + public static A foo() { + return new A.Builder<>().create(); + } + + static class A> { + public static class Builder> { + public A create() { + return null; + } + } + } +} + + diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/GenericsHighlightingTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/GenericsHighlightingTest.java index 944e0756a3b0..3964bad42031 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/GenericsHighlightingTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/GenericsHighlightingTest.java @@ -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); } diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/GenericsHighlighting8Test.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/GenericsHighlighting8Test.java index 49027135eecf..fb5046f194e9 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/GenericsHighlighting8Test.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/GenericsHighlighting8Test.java @@ -766,6 +766,10 @@ public class GenericsHighlighting8Test extends LightDaemonAnalyzerTestCase { doTest(true); } + public void testInferredParameterInBoundsInRecursiveGenerics() { + doTest(false); + } + private void doTest() { doTest(false); }