diff --git a/java/java-psi-api/src/com/intellij/psi/PsiCapturedWildcardType.java b/java/java-psi-api/src/com/intellij/psi/PsiCapturedWildcardType.java index e38057f075ac..2e0f4afa8e09 100644 --- a/java/java-psi-api/src/com/intellij/psi/PsiCapturedWildcardType.java +++ b/java/java-psi-api/src/com/intellij/psi/PsiCapturedWildcardType.java @@ -57,8 +57,8 @@ public class PsiCapturedWildcardType extends PsiType.Stub { public static RecursionGuard guard = RecursionManager.createGuard("captureGuard"); - public static boolean isNoCapture() { - return !guard.currentStack().isEmpty(); + public static boolean isCapture() { + return guard.currentStack().isEmpty(); } @Nullable diff --git a/java/java-psi-api/src/com/intellij/psi/util/PsiUtil.java b/java/java-psi-api/src/com/intellij/psi/util/PsiUtil.java index d3a5a6c58c2c..6141e725b213 100644 --- a/java/java-psi-api/src/com/intellij/psi/util/PsiUtil.java +++ b/java/java-psi-api/src/com/intellij/psi/util/PsiUtil.java @@ -609,8 +609,10 @@ public final class PsiUtil extends PsiUtilCore { * would be equivalent */ public static boolean equalOnEquivalentClasses(PsiClassType thisClassType, @NotNull PsiClass aClass, PsiClassType otherClassType, @NotNull PsiClass bClass) { - final PsiClassType capture1 = PsiCapturedWildcardType.isNoCapture() ? thisClassType : (PsiClassType)captureToplevelWildcards(thisClassType, aClass); - final PsiClassType capture2 = PsiCapturedWildcardType.isNoCapture() ? otherClassType : (PsiClassType)captureToplevelWildcards(otherClassType, bClass); + final PsiClassType capture1 = !PsiCapturedWildcardType.isCapture() + ? thisClassType : (PsiClassType)captureToplevelWildcards(thisClassType, aClass); + final PsiClassType capture2 = !PsiCapturedWildcardType.isCapture() + ? otherClassType : (PsiClassType)captureToplevelWildcards(otherClassType, bClass); final PsiClassType.ClassResolveResult result1 = capture1.resolveGenerics(); final PsiClassType.ClassResolveResult result2 = capture2.resolveGenerics(); diff --git a/java/java-psi-api/src/com/intellij/psi/util/TypeConversionUtil.java b/java/java-psi-api/src/com/intellij/psi/util/TypeConversionUtil.java index 0b91b3645e19..fa8711e10270 100644 --- a/java/java-psi-api/src/com/intellij/psi/util/TypeConversionUtil.java +++ b/java/java-psi-api/src/com/intellij/psi/util/TypeConversionUtil.java @@ -704,6 +704,13 @@ public class TypeConversionUtil { } public static boolean isAssignable(@NotNull PsiType left, @NotNull PsiType right, boolean allowUncheckedConversion) { + return isAssignable(left, right, allowUncheckedConversion, true); + } + + private static boolean isAssignable(@NotNull PsiType left, + @NotNull PsiType right, + boolean allowUncheckedConversion, + boolean capture) { if (left == right || left.equals(right)) return true; if (isNullType(right)) { @@ -737,24 +744,24 @@ public class TypeConversionUtil { if (left instanceof PsiIntersectionType) { PsiType[] conjuncts = ((PsiIntersectionType)left).getConjuncts(); for (PsiType conjunct : conjuncts) { - if (!isAssignable(conjunct, right, allowUncheckedConversion)) return false; + if (!isAssignable(conjunct, right, allowUncheckedConversion, capture)) return false; } return true; } if (right instanceof PsiIntersectionType) { PsiType[] conjuncts = ((PsiIntersectionType)right).getConjuncts(); for (PsiType conjunct : conjuncts) { - if (isAssignable(left, conjunct, allowUncheckedConversion)) return true; + if (isAssignable(left, conjunct, allowUncheckedConversion, capture)) return true; } return false; } if (right instanceof PsiCapturedWildcardType) { - return isAssignable(left, ((PsiCapturedWildcardType)right).getUpperBound(), allowUncheckedConversion); + return isAssignable(left, ((PsiCapturedWildcardType)right).getUpperBound(), allowUncheckedConversion, capture); } if (left instanceof PsiCapturedWildcardType) { - return left.equals(right) || isAssignable(((PsiCapturedWildcardType)left).getLowerBound(), right, allowUncheckedConversion); + return left.equals(right) || isAssignable(((PsiCapturedWildcardType)left).getLowerBound(), right, allowUncheckedConversion, capture); } if (left instanceof PsiWildcardType) { @@ -781,17 +788,17 @@ public class TypeConversionUtil { if (lCompType instanceof PsiPrimitiveType) { return lCompType.equals(rCompType); } - return !(rCompType instanceof PsiPrimitiveType) && isAssignable(lCompType, rCompType, allowUncheckedConversion); + return !(rCompType instanceof PsiPrimitiveType) && isAssignable(lCompType, rCompType, allowUncheckedConversion, capture); } if (left instanceof PsiDisjunctionType) { for (PsiType type : ((PsiDisjunctionType)left).getDisjunctions()) { - if (isAssignable(type, right, allowUncheckedConversion)) return true; + if (isAssignable(type, right, allowUncheckedConversion, capture)) return true; } return false; } if (right instanceof PsiDisjunctionType) { - return isAssignable(left, ((PsiDisjunctionType)right).getLeastUpperBound(), allowUncheckedConversion); + return isAssignable(left, ((PsiDisjunctionType)right).getLeastUpperBound(), allowUncheckedConversion, capture); } if (left instanceof PsiArrayType) return false; @@ -830,7 +837,7 @@ public class TypeConversionUtil { && rText.endsWith(lText) && rText.charAt(rText.length() - lText.length() - 1) == '.'; } - return isClassAssignable(leftResult, rightResult, allowUncheckedConversion, left.getResolveScope()); + return isClassAssignable(leftResult, rightResult, allowUncheckedConversion, left.getResolveScope(), capture); } private static boolean isAssignableFromWildcard(@NotNull PsiType left, @NotNull PsiWildcardType rightWildcardType) { @@ -923,19 +930,22 @@ public class TypeConversionUtil { private static boolean isClassAssignable(@NotNull PsiClassType.ClassResolveResult leftResult, @NotNull PsiClassType.ClassResolveResult rightResult, - boolean allowUncheckedConversion, GlobalSearchScope resolveScope) { + boolean allowUncheckedConversion, + GlobalSearchScope resolveScope, + boolean capture) { final PsiClass leftClass = leftResult.getElement(); final PsiClass rightClass = rightResult.getElement(); if (leftClass == null || rightClass == null) return false; PsiSubstitutor superSubstitutor = JavaClassSupers.getInstance().getSuperClassSubstitutor(leftClass, rightClass, resolveScope, rightResult.getSubstitutor()); - return superSubstitutor != null && typeParametersAgree(leftResult, rightResult, allowUncheckedConversion, superSubstitutor); + return superSubstitutor != null && typeParametersAgree(leftResult, rightResult, allowUncheckedConversion, superSubstitutor, capture); } private static boolean typeParametersAgree(@NotNull PsiClassType.ClassResolveResult leftResult, @NotNull PsiClassType.ClassResolveResult rightResult, - boolean allowUncheckedConversion, PsiSubstitutor superSubstitutor) { + boolean allowUncheckedConversion, PsiSubstitutor superSubstitutor, + boolean capture) { PsiSubstitutor rightSubstitutor = rightResult.getSubstitutor(); PsiClass leftClass = leftResult.getElement(); PsiClass rightClass = rightResult.getElement(); @@ -958,8 +968,9 @@ public class TypeConversionUtil { PsiTypeParameter rp = ri.next(); final PsiType typeLeft = leftSubstitutor.substitute(lp); if (typeLeft == null) continue; - final PsiType typeRight = PsiCapturedWildcardType.isNoCapture() ? rightSubstitutor.substitute(rp) - : rightSubstitutor.substituteWithBoundsPromotion(rp); + final PsiType typeRight = PsiCapturedWildcardType.isCapture() && capture + ? rightSubstitutor.substituteWithBoundsPromotion(rp) + : rightSubstitutor.substitute(rp); if (typeRight == null) { // compatibility feature: allow to assign raw types to generic ones return allowUncheckedConversion; @@ -988,7 +999,7 @@ public class TypeConversionUtil { if (typeRight instanceof PsiWildcardType) { final PsiWildcardType rightWildcard = (PsiWildcardType)typeRight; if (leftWildcard.isExtends()) { - return rightWildcard.isExtends() && isAssignable(leftBound, rightWildcard.getBound(), allowUncheckedConversion); + return rightWildcard.isExtends() && isAssignable(leftBound, rightWildcard.getBound(), allowUncheckedConversion, false); } else { //isSuper if (rightWildcard.isSuper()) { @@ -996,7 +1007,7 @@ public class TypeConversionUtil { @NotNull @Override public Boolean compute() { - return isAssignable(rightWildcard.getBound(), leftBound, allowUncheckedConversion); + return isAssignable(rightWildcard.getBound(), leftBound, allowUncheckedConversion, false); } }); if (assignable != null && assignable) { @@ -1008,14 +1019,14 @@ public class TypeConversionUtil { } else { if (leftWildcard.isExtends()) { - return isAssignable(leftBound, typeRight, false); + return isAssignable(leftBound, typeRight, false, false); } else { // isSuper final Boolean assignable = ourGuard.doPreventingRecursion(leftWildcard, true, new NotNullComputable() { @NotNull @Override public Boolean compute() { - return isAssignable(typeRight, leftBound, false); + return isAssignable(typeRight, leftBound, false, false); } }); return assignable == null || assignable.booleanValue(); diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting/StopBoundsPromotionInsideNestedWildcards.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting/StopBoundsPromotionInsideNestedWildcards.java new file mode 100644 index 000000000000..155368d853cb --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting/StopBoundsPromotionInsideNestedWildcards.java @@ -0,0 +1,8 @@ +interface A> { } +interface B> { } + +class C { + void foo(A x){ + A>> y = x; + } +} \ No newline at end of file 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 f702062b25d6..0c5612e5a48e 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/GenericsHighlightingTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/GenericsHighlightingTest.java @@ -539,6 +539,10 @@ public class GenericsHighlightingTest extends LightDaemonAnalyzerTestCase { doTest(LanguageLevel.JDK_1_7, JavaSdkVersion.JDK_1_7, false); } + public void testStopBoundsPromotionInsideNestedWildcards() throws Exception { + doTest(LanguageLevel.JDK_1_7, JavaSdkVersion.JDK_1_7, false); + } + public void testIDEA130243() throws Exception { doTest(LanguageLevel.JDK_1_7, JavaSdkVersion.JDK_1_7, false); }