From 87f484a5fec8233f95ec0bcc918bca0551fc1ab3 Mon Sep 17 00:00:00 2001 From: Anna Kozlova Date: Wed, 9 Sep 2015 16:45:40 +0300 Subject: [PATCH] diamonds with anonymous: reject cases according to JDK-8062373 --- .../com/intellij/psi/PsiDiamondTypeImpl.java | 73 ++++++++++++++++++- .../graphInference/InferenceSession.java | 4 + .../graphInference/PsiPolyExpressionUtil.java | 2 +- ...AnonymousRejectInferredFreshVariables.java | 6 ++ ...dsWithAnonymousRejectIntersectionType.java | 9 +++ ...sWithAnonymousRejectNotAccessibleType.java | 15 ++++ .../daemon/LightAdvHighlightingJdk9Test.java | 3 + 7 files changed, 110 insertions(+), 2 deletions(-) create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting9/DiamondsWithAnonymousRejectInferredFreshVariables.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting9/DiamondsWithAnonymousRejectIntersectionType.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting9/DiamondsWithAnonymousRejectNotAccessibleType.java diff --git a/java/java-psi-impl/src/com/intellij/psi/PsiDiamondTypeImpl.java b/java/java-psi-impl/src/com/intellij/psi/PsiDiamondTypeImpl.java index 44b156ef26e5..de292ca9f9b9 100644 --- a/java/java-psi-impl/src/com/intellij/psi/PsiDiamondTypeImpl.java +++ b/java/java-psi-impl/src/com/intellij/psi/PsiDiamondTypeImpl.java @@ -26,6 +26,7 @@ import com.intellij.openapi.util.Ref; import com.intellij.openapi.util.text.StringUtil; import com.intellij.pom.java.LanguageLevel; import com.intellij.psi.codeStyle.JavaCodeStyleManager; +import com.intellij.psi.impl.source.resolve.graphInference.InferenceSession; import com.intellij.psi.infos.CandidateInfo; import com.intellij.psi.infos.MethodCandidateInfo; import com.intellij.psi.scope.PsiConflictResolver; @@ -133,7 +134,17 @@ public class PsiDiamondTypeImpl extends PsiDiamondType { return DiamondInferenceResult.EXPLICIT_CONSTRUCTOR_TYPE_ARGS; } - return resolveInferredTypesNoCheck(newExpression, context); + final DiamondInferenceResult inferenceResult = resolveInferredTypesNoCheck(newExpression, context); + if (anonymousClass != null && PsiUtil.isLanguageLevel9OrHigher(newExpression)) { + final InferredAnonymTypeVisitor anonymTypeVisitor = new InferredAnonymTypeVisitor(context); + for (PsiType type : inferenceResult.getInferredTypes()) { + final Boolean accepted = type.accept(anonymTypeVisitor); + if (accepted != null && !accepted.booleanValue()) { + return PsiDiamondTypeImpl.DiamondInferenceResult.ANONYMOUS_INNER_RESULT; + } + } + } + return inferenceResult; } public static DiamondInferenceResult resolveInferredTypesNoCheck(final PsiNewExpression newExpression, final PsiElement context) { @@ -457,4 +468,64 @@ public class PsiDiamondTypeImpl extends PsiDiamondType { } return false; } + + /** + * from JDK-8062373 Allow diamond to be used with anonymous classes + * It is a compile-time error if the superclass or superinterface type of the anonymous class, T, or any subexpression of T, has one of the following forms: + * - A type variable (4.4) that was not declared as a type parameter (such as a type variable produced by capture conversion (5.1.10)) + * - An intersection type (4.9) + * - A class or interface type, where the class or interface declaration is not accessible from the class or interface in which the expression appears. + * + * The term "subexpression" includes type arguments of parameterized types (4.5), bounds of wildcards (4.5.1), and element types of array types (10.1). + * It excludes bounds of type variables. + */ + private static class InferredAnonymTypeVisitor extends PsiTypeVisitor { + private final PsiElement myExpression; + + public InferredAnonymTypeVisitor(PsiElement expression) { + myExpression = expression; + } + + @Nullable + @Override + public Boolean visitType(PsiType type) { + return true; + } + + @Nullable + @Override + public Boolean visitCapturedWildcardType(PsiCapturedWildcardType capturedWildcardType) { + return false; + } + + @Nullable + @Override + public Boolean visitIntersectionType(PsiIntersectionType intersectionType) { + return false; + } + + @Nullable + @Override + public Boolean visitClassType(PsiClassType classType) { + final PsiClassType.ClassResolveResult resolveResult = classType.resolveGenerics(); + final PsiClass psiClass = resolveResult.getElement(); + if (psiClass != null) { + if (psiClass instanceof PsiTypeParameter && InferenceSession.isFreshVariable((PsiTypeParameter)psiClass)) { + return false; + } + + if (!PsiUtil.isAccessible(psiClass, myExpression, null)) { + return false; + } + + for (PsiType psiType : resolveResult.getSubstitutor().getSubstitutionMap().values()) { + final Boolean accepted = psiType.accept(this); + if (accepted != null && !accepted.booleanValue()) { + return false; + } + } + } + return true; + } + } } 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 e028f0b482cd..4cdf239c4a56 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 @@ -1628,6 +1628,10 @@ public class InferenceSession { final PsiElement originalContext = p1.getUserData(ORIGINAL_CONTEXT); return originalContext != null && originalContext == p2.getUserData(ORIGINAL_CONTEXT); } + + public static boolean isFreshVariable(PsiTypeParameter typeParameter) { + return typeParameter.getUserData(ORIGINAL_CONTEXT) != null; + } public static PsiClass findParameterizationOfTheSameGenericClass(List upperBounds, Processor> processor) { diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/PsiPolyExpressionUtil.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/PsiPolyExpressionUtil.java index d206ec4632a2..c5ef8505cc75 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/PsiPolyExpressionUtil.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/PsiPolyExpressionUtil.java @@ -47,7 +47,7 @@ public class PsiPolyExpressionUtil { return isPolyExpression(((PsiParenthesizedExpression)expression).getExpression()); } else if (expression instanceof PsiNewExpression) { - final PsiJavaCodeReferenceElement classReference = ((PsiNewExpression)expression).getClassReference(); + final PsiJavaCodeReferenceElement classReference = ((PsiNewExpression)expression).getClassOrAnonymousClassReference(); if (classReference != null) { final PsiReferenceParameterList parameterList = classReference.getParameterList(); if (parameterList != null) { diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting9/DiamondsWithAnonymousRejectInferredFreshVariables.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting9/DiamondsWithAnonymousRejectInferredFreshVariables.java new file mode 100644 index 000000000000..c5e66e24c1e0 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting9/DiamondsWithAnonymousRejectInferredFreshVariables.java @@ -0,0 +1,6 @@ +class A {} +class Foo> { + { + Foo foo = new Foo<>() {}; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting9/DiamondsWithAnonymousRejectIntersectionType.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting9/DiamondsWithAnonymousRejectIntersectionType.java new file mode 100644 index 000000000000..62d201e0d23d --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting9/DiamondsWithAnonymousRejectIntersectionType.java @@ -0,0 +1,9 @@ +import java.util.List; + +class Foo & Runnable> { + Foo() {} + + { + Foo foo = new Foo<>() {}; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting9/DiamondsWithAnonymousRejectNotAccessibleType.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting9/DiamondsWithAnonymousRejectNotAccessibleType.java new file mode 100644 index 000000000000..0404c39dcebf --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting9/DiamondsWithAnonymousRejectNotAccessibleType.java @@ -0,0 +1,15 @@ + +class B { + private static class A {} + + public static class C extends A {} + public static class D extends A {} +} + +class Foo { + Foo(E e, E e1) {} + + { + Foo foo = new Foo<>(new B.C(), new B.D()) {}; + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/LightAdvHighlightingJdk9Test.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/LightAdvHighlightingJdk9Test.java index 2fb951037845..feb7945f8015 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/LightAdvHighlightingJdk9Test.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/LightAdvHighlightingJdk9Test.java @@ -49,6 +49,9 @@ public class LightAdvHighlightingJdk9Test extends LightDaemonAnalyzerTestCase { public void testTryWithResources() { doTest(false, false); } public void testDiamondsWithAnonymous() { doTest(false, false);} + public void testDiamondsWithAnonymousRejectInferredFreshVariables() { doTest(false, false);} + public void testDiamondsWithAnonymousRejectNotAccessibleType() { doTest(false, false);} + public void testDiamondsWithAnonymousRejectIntersectionType() { doTest(false, false);} public void testValueTypes() { setLanguageLevel(LanguageLevel.JDK_X); doTest(false, false); } }