diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/GenericsHighlightUtil.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/GenericsHighlightUtil.java index e02b0564ca32..04ec78cb6a26 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/GenericsHighlightUtil.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/GenericsHighlightUtil.java @@ -172,35 +172,30 @@ public class GenericsHighlightUtil { final PsiSubstitutor substitutor, final PsiType type, final PsiElement typeElement2Highlight) { - PsiType type2highlight = type; - if (type instanceof PsiWildcardType) { - if (((PsiWildcardType)type).isExtends()) { - type2highlight = ((PsiWildcardType)type).getExtendsBound(); - } else if (((PsiWildcardType)type).isSuper()) { - type2highlight = ((PsiWildcardType)type).getSuperBound(); - } + final PsiClass referenceClass; + if (type instanceof PsiClassType){ + referenceClass = ((PsiClassType)type).resolve(); + } else { + referenceClass = null; } - if (!(type2highlight instanceof PsiClassType)) return null; - final PsiClass referenceClass = ((PsiClassType)type2highlight).resolve(); - if (referenceClass == null) return null; final PsiClassType[] bounds = classParameter.getSuperTypes(); for (PsiClassType type1 : bounds) { PsiType bound = substitutor.substitute(type1); - if (!TypeConversionUtil.isAssignable(bound, type2highlight, false) && TypesDistinctProver.provablyDistinct(bound, type)) { + if (checkNotInBounds(type, bound)) { PsiClass boundClass = bound instanceof PsiClassType ? ((PsiClassType)bound).resolve() : null; - @NonNls final String messageKey = boundClass == null || referenceClass.isInterface() == boundClass.isInterface() + @NonNls final String messageKey = boundClass == null || referenceClass == null || referenceClass.isInterface() == boundClass.isInterface() ? "generics.type.parameter.is.not.within.its.bound.extend" : "generics.type.parameter.is.not.within.its.bound.implement"; String description = JavaErrorMessages.message(messageKey, - HighlightUtil.formatClass(referenceClass), + referenceClass != null ? HighlightUtil.formatClass(referenceClass) : type.getPresentableText(), HighlightUtil.formatType(bound)); final HighlightInfo highlightInfo = HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, typeElement2Highlight, description); - if (bound instanceof PsiClassType) { + if (bound instanceof PsiClassType && referenceClass != null) { QuickFixAction .registerQuickFixAction(highlightInfo, QUICK_FIX_FACTORY.createExtendsListFix(referenceClass, (PsiClassType)bound, true), null); @@ -211,6 +206,78 @@ public class GenericsHighlightUtil { return null; } + private static boolean checkNotInBounds(PsiType type, PsiType bound) { + if (type instanceof PsiClassType) { + return checkNotAssignable(bound, type); + } + else { + if (type instanceof PsiWildcardType) { + if (((PsiWildcardType)type).isExtends()) { + return checkExtendsWildcardCaptureFailure((PsiWildcardType)type, bound); + } + else if (((PsiWildcardType)type).isSuper()) { + return checkNotAssignable(bound, ((PsiWildcardType)type).getSuperBound()); + } + } + else if (type instanceof PsiArrayType) { + return checkNotAssignable(bound, type, true); + } + } + return false; + } + + //JLS 5.1.10 + private static boolean checkExtendsWildcardCaptureFailure(PsiWildcardType type, PsiType bound) { + LOG.assertTrue(type.isExtends()); + final PsiType extendsBound = type.getExtendsBound(); + PsiType boundBound = bound; + if (bound instanceof PsiWildcardType) { + if (((PsiWildcardType)bound).isBounded()) { + boundBound = ((PsiWildcardType)bound).isSuper() + ? ((PsiWildcardType)bound).getSuperBound() + : ((PsiWildcardType)bound).getExtendsBound(); + } else { + return false; + } + } + return !TypeConversionUtil.areTypesConvertible(boundBound, extendsBound) && + !TypeConversionUtil.areTypesConvertible(extendsBound, boundBound); + } + + private static boolean checkNotAssignable(final PsiType bound, final PsiType type) { + return checkNotAssignable(bound, type, allowUncheckedConversions(type)); + } + + private static boolean checkNotAssignable(final PsiType bound, + final PsiType type, + final boolean allowUncheckedConversion) { + if (bound instanceof PsiWildcardType) { + if (((PsiWildcardType)bound).isBounded()) { + final PsiType boundBound = ((PsiWildcardType)bound).isExtends() + ? ((PsiWildcardType)bound).getExtendsBound() + : ((PsiWildcardType)bound).getSuperBound(); + return !TypeConversionUtil.isAssignable(boundBound, type, allowUncheckedConversion); + } else { + return true; + } + } else { + return !TypeConversionUtil.isAssignable(bound, type, allowUncheckedConversion); + } + } + + private static boolean allowUncheckedConversions(PsiType type) { + boolean allowUncheckedConversions = true; + if (type instanceof PsiClassType) { + final PsiClass classType = ((PsiClassType)type).resolve(); + if (classType != null) { + for (PsiTypeParameter parameter : PsiUtil.typeParametersIterable(classType)) { + allowUncheckedConversions &= parameter.getExtendsListTypes().length == 0; + } + } + } + return allowUncheckedConversions; + } + private static String typeParameterListOwnerDescription(final PsiTypeParameterListOwner typeParameterListOwner) { if (typeParameterListOwner instanceof PsiClass) { return HighlightUtil.formatClass((PsiClass)typeParameterListOwner); diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting7/DiamondNeg1.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting7/DiamondNeg1.java index 1f674a9afcea..88e094ca9665 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting7/DiamondNeg1.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting7/DiamondNeg1.java @@ -8,31 +8,31 @@ class Neg01 { void test() { Neg01<String> n1 = new Neg01<>("" ); //new Foo created - Neg01<? extends String> n2 = new Neg01<>(""); //new Foo created + Neg01<? extends String> n2 = new Neg01<>(""); //new Foo created Neg01 n3 = new Neg01<>(""); //new Foo created - Neg01<? super String> n4 = new Neg01<>(""); //new Foo created + Neg01<? super String> n4 = new Neg01<>(""); //new Foo created Neg01<String> n5 = new Neg01<>("") { }; //new Foo created - Neg01<? extends String> n6 = new Neg01<>("") { + Neg01<? extends String> n6 = new Neg01<>("") { }; //new Foo created Neg01 n7 = new Neg01<>("") { }; //new Foo created - Neg01<? super String> n8 = new Neg01<>("") { + Neg01<? super String> n8 = new Neg01<>("") { }; //new Foo created Neg01<String> n9 = new Neg01<>("", ""); //new Foo created - Neg01<? extends String> n10 = new Neg01<>("", ""); //new Foo created + Neg01<? extends String> n10 = new Neg01<>("", ""); //new Foo created Neg01 n11 = new Neg01<>("", ""); //new Foo created Foo n12 = new Neg01<>("", ""); //new Foo created Neg01<String> n13 = new Neg01<>("", "") { }; //new Foo created - Neg01<? extends String> n14 = new Neg01<>("", "") { + Neg01<? extends String> n14 = new Neg01<>("", "") { }; //new Foo created Neg01 n15 = new Neg01<>("", "") { }; //new Foo created - Neg01<? super String> n16 = new Neg01<>("", "") { + Neg01<? super String> n16 = new Neg01<>("", "") { }; //new Foo created } } diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting7/DiamondNeg2.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting7/DiamondNeg2.java index 13f71c79f4aa..7503936b1d6c 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting7/DiamondNeg2.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting7/DiamondNeg2.java @@ -10,61 +10,61 @@ class Neg02 { void testSimple() { Foo<String> f1 = new Foo<>(""); //new Foo created - Foo<? extends String> f2 = new Foo<>(""); //new Foo created + Foo<? extends String> f2 = new Foo<>(""); //new Foo created Foo f3 = new Foo<>(""); //new Foo created - Foo<? super String> f4 = new Foo<>(""); //new Foo created + Foo<? super String> f4 = new Foo<>(""); //new Foo created Foo<String> f5 = new Foo<>("") { }; //new Foo created - Foo<? extends String> f6 = new Foo<>("") { + Foo<? extends String> f6 = new Foo<>("") { }; //new Foo created Foo f7 = new Foo< >("") { }; //new Foo created - Foo<? super String> f8 = new Foo<>("") { + Foo<? super String> f8 = new Foo<>("") { }; //new Foo created Foo<String> f9 = new Foo<>("", ""); //new Foo created - Foo<? extends String> f10 = new Foo<>("", ""); //new Foo created + Foo<? extends String> f10 = new Foo<>("", ""); //new Foo created Foo f11 = new Foo< >("", ""); //new Foo created - Foo<? super String> f12 = new Foo<>("", ""); //new Foo created + Foo<? super String> f12 = new Foo<>("", ""); //new Foo created Foo<String> f13 = new Foo<>("", "") { }; //new Foo created - Foo<? extends String> f14 = new Foo<>("", "") { + Foo<? extends String> f14 = new Foo<>("", "") { }; //new Foo created Foo f15 = new Foo< >("", "") { }; //new Foo created - Foo<? super String> f16 = new Foo<>("", "") { + Foo<? super String> f16 = new Foo<>("", "") { }; //new Foo created } void testQualified() { Foo<String> f1 = new Neg02.Foo<>(""); //new Foo created - Foo<? extends String> f2 = new Neg02.Foo<>(""); //new Foo created + Foo<? extends String> f2 = new Neg02.Foo<>(""); //new Foo created Foo f3 = new Neg02.Foo< >(""); //new Foo created - Foo<? super String> f4 = new Neg02.Foo<>(""); //new Foo created + Foo<? super String> f4 = new Neg02.Foo<>(""); //new Foo created Foo<String> f5 = new Neg02.Foo<>("") { }; //new Foo created - Foo<? extends String> f6 = new Neg02.Foo<>("") { + Foo<? extends String> f6 = new Neg02.Foo<>("") { }; //new Foo created Foo f7 = new Neg02.Foo< >("") { }; //new Foo created - Foo<? super String> f8 = new Neg02.Foo<>("") { + Foo<? super String> f8 = new Neg02.Foo<>("") { }; //new Foo created Foo<String> f9 = new Neg02.Foo<>("", ""); //new Foo created - Foo<? extends String> f10 = new Neg02.Foo<>("", ""); //new Foo created + Foo<? extends String> f10 = new Neg02.Foo<>("", ""); //new Foo created Foo f11 = new Neg02.Foo< >("", ""); //new Foo created - Foo<? super String> f12 = new Neg02.Foo<>("", ""); //new Foo created + Foo<? super String> f12 = new Neg02.Foo<>("", ""); //new Foo created Foo<String> f13 = new Neg02.Foo<>("", "") { }; //new Foo created - Foo<? extends String> f14 = new Neg02.Foo<>("", "") { + Foo<? extends String> f14 = new Neg02.Foo<>("", "") { }; //new Foo created Foo f15 = new Neg02.Foo< >("", "") { }; //new Foo created - Foo<? super String> f16 = new Neg02.Foo<>("", "") { + Foo<? super String> f16 = new Neg02.Foo<>("", "") { }; //new Foo created } } diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting7/DiamondNeg3.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting7/DiamondNeg3.java index 02cf6e5e1574..d699f6314fb2 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting7/DiamondNeg3.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting7/DiamondNeg3.java @@ -7,67 +7,67 @@ class Neg03 { void testSimple() { Foo<String> f1 = new Foo<>(""); //new Foo created - Foo<? extends String> f2 = new Foo<>(""); //new Foo created + Foo<? extends String> f2 = new Foo<>(""); //new Foo created Foo f3 = new Foo<>(""); //new Foo created - Foo<? super String> f4 = new Foo<>(""); //new Foo created + Foo<? super String> f4 = new Foo<>(""); //new Foo created Foo<String> f5 = new Foo<>(""){}; //new Foo created - Foo<? extends String> f6 = new Foo<>(""){}; //new Foo created + Foo<? extends String> f6 = new Foo<>(""){}; //new Foo created Foo f7 = new Foo<>(""){}; //new Foo created - Foo<? super String> f8 = new Foo<>(""){}; //new Foo created + Foo<? super String> f8 = new Foo<>(""){}; //new Foo created Foo<String> f9 = new Foo<>("", ""); //new Foo created - Foo<? extends String> f10 = new Foo<>("", ""); //new Foo created + Foo<? extends String> f10 = new Foo<>("", ""); //new Foo created Foo f11 = new Foo<>("", ""); //new Foo created - Foo<? super String> f12 = new Foo<>("", ""); //new Foo created + Foo<? super String> f12 = new Foo<>("", ""); //new Foo created Foo<String> f13 = new Foo<>("", ""){}; //new Foo created - Foo<? extends String> f14 = new Foo<>("", ""){}; //new Foo created + Foo<? extends String> f14 = new Foo<>("", ""){}; //new Foo created Foo f15 = new Foo<>("", ""){}; //new Foo created - Foo<? super String> f16 = new Foo<>("", ""){}; //new Foo created + Foo<? super String> f16 = new Foo<>("", ""){}; //new Foo created } void testQualified_1() { Foo<String> f1 = new Neg03.Foo<>(""); //new Foo created - Foo<? extends String> f2 = new Neg03.Foo<>(""); //new Foo created + Foo<? extends String> f2 = new Neg03.Foo<>(""); //new Foo created Foo f3 = new Neg03.Foo<>(""); //new Foo created - Foo<? super String> f4 = new Neg03.Foo<>(""); //new Foo created + Foo<? super String> f4 = new Neg03.Foo<>(""); //new Foo created Foo<String> f5 = new Neg03.Foo<>(""){}; //new Foo created - Foo<? extends String> f6 = new Neg03.Foo<>(""){}; //new Foo created + Foo<? extends String> f6 = new Neg03.Foo<>(""){}; //new Foo created Foo f7 = new Neg03.Foo<>(""){}; //new Foo created - Foo<? super String> f8 = new Neg03.Foo<>(""){}; //new Foo created + Foo<? super String> f8 = new Neg03.Foo<>(""){}; //new Foo created Foo<String> f9 = new Neg03.Foo<>("", ""); //new Foo created - Foo<? extends String> f10 = new Neg03.Foo<>("", ""); //new Foo created + Foo<? extends String> f10 = new Neg03.Foo<>("", ""); //new Foo created Foo f11 = new Neg03.Foo<>("", ""); //new Foo created - Foo<? super String> f12 = new Neg03.Foo<>("", ""); //new Foo created + Foo<? super String> f12 = new Neg03.Foo<>("", ""); //new Foo created Foo<String> f13 = new Neg03.Foo<>("", ""){}; //new Foo created - Foo<? extends String> f14 = new Neg03.Foo<>("", ""){}; //new Foo created + Foo<? extends String> f14 = new Neg03.Foo<>("", ""){}; //new Foo created Foo f15 = new Neg03.Foo<>("", ""){}; //new Foo created - Foo<? super String> f16 = new Neg03.Foo<>("", ""){}; //new Foo created + Foo<? super String> f16 = new Neg03.Foo<>("", ""){}; //new Foo created } void testQualified_2(Neg03 n) { Foo<String> f1 = n.new Foo<>(""); //new Foo created - Foo<? extends String> f2 = n.new Foo<>(""); //new Foo created + Foo<? extends String> f2 = n.new Foo<>(""); //new Foo created Foo f3 = n.new Foo<>(""); //new Foo created - Foo<? super String> f4 = n.new Foo<>(""); //new Foo created + Foo<? super String> f4 = n.new Foo<>(""); //new Foo created Foo<String> f5 = n.new Foo<>(""){}; //new Foo created - Foo<? extends String> f6 = n.new Foo<>(""){}; //new Foo created + Foo<? extends String> f6 = n.new Foo<>(""){}; //new Foo created Foo f7 = n.new Foo<>(""){}; //new Foo created - Foo<? super String> f8 = n.new Foo<>(""){}; //new Foo created + Foo<? super String> f8 = n.new Foo<>(""){}; //new Foo created Foo<String> f9 = n.new Foo<>("", ""); //new Foo created - Foo<? extends String> f10 = n.new Foo<>("", ""); //new Foo created + Foo<? extends String> f10 = n.new Foo<>("", ""); //new Foo created Foo f11 = n.new Foo<>("", ""); //new Foo created - Foo<? super String> f12 = n.new Foo<>("", ""); //new Foo created + Foo<? super String> f12 = n.new Foo<>("", ""); //new Foo created Foo<String> f13 = n.new Foo<>("", ""){}; //new Foo created - Foo<? extends String> f14 = n.new Foo<>("", ""){}; //new Foo created + Foo<? extends String> f14 = n.new Foo<>("", ""){}; //new Foo created Foo f15 = n.new Foo<>("", ""){}; //new Foo created - Foo<? super String> f16 = n.new Foo<>("", ""){}; //new Foo created + Foo<? super String> f16 = n.new Foo<>("", ""){}; //new Foo created } } diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting7/DiamondNeg4.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting7/DiamondNeg4.java index 000cb1c0b60f..9f05af301260 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting7/DiamondNeg4.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting7/DiamondNeg4.java @@ -6,23 +6,23 @@ class Neg04 { Foo(V x, Z z) {} } Foo<String> n1 = new Foo<>(""); //new Foo created - Foo<? extends String> n2 = new Foo<>(""); //new Foo created + Foo<? extends String> n2 = new Foo<>(""); //new Foo created Foo n3 = new Foo<>(""); //new Foo created - Foo<? super String> n4 = new Foo<>(""); //new Foo created + Foo<? super String> n4 = new Foo<>(""); //new Foo created Foo<String> n5 = new Foo<>(""){}; //new Foo created - Foo<? extends String> n6 = new Foo<>(""){}; //new Foo created + Foo<? extends String> n6 = new Foo<>(""){}; //new Foo created Foo n7 = new Foo<>(""){}; //new Foo created - Foo<? super String> n8 = new Foo<>(""){}; //new Foo created + Foo<? super String> n8 = new Foo<>(""){}; //new Foo created Foo<String> n9 = new Foo<>("", ""); //new Foo created - Foo<? extends String> n10 = new Foo<>("", ""); //new Foo created + Foo<? extends String> n10 = new Foo<>("", ""); //new Foo created Foo n11 = new Foo<>("", ""); //new Foo created - Foo<? super String> n12 = new Foo<>("", ""); //new Foo created + Foo<? super String> n12 = new Foo<>("", ""); //new Foo created Foo<String> n13 = new Foo<>("", ""){}; //new Foo created - Foo<? extends String> n14 = new Foo<>("", ""){}; //new Foo created + Foo<? extends String> n14 = new Foo<>("", ""){}; //new Foo created Foo n15 = new Foo<>("", ""){}; //new Foo created - Foo<? super String> n16 = new Foo<>("", ""){}; //new Foo created + Foo<? super String> n16 = new Foo<>("", ""){}; //new Foo created } } diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting/TypeWithinItsWildcardBound.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting/TypeWithinItsWildcardBound.java index b14c3224bad9..47ecb3b838c3 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting/TypeWithinItsWildcardBound.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting/TypeWithinItsWildcardBound.java @@ -1,6 +1,8 @@ public class WithingBounds { interface I { } + interface I1 { + } void testE1() { class A { @@ -22,7 +24,7 @@ public class WithingBounds { class ToCheckExtends { } - ToCheckExtends<? extends C> pr; + ToCheckExtends<? extends C> pr; ToCheckExtends> pr1; } @@ -35,7 +37,7 @@ public class WithingBounds { class ToCheckExtends { } - ToCheckExtends<? extends B> pr; + ToCheckExtends<? extends B> pr; } void testE22() { @@ -65,7 +67,7 @@ public class WithingBounds { class ToCheckExtends { } - ToCheckExtends<? extends I> pr; + ToCheckExtends<? extends I> pr; } @@ -109,7 +111,7 @@ public class WithingBounds { class ToCheckExtends { } - ToCheckExtends<? extends A> pr; + ToCheckExtends<? extends A> pr; } void testE5() { @@ -153,7 +155,7 @@ public class WithingBounds { class ToCheckExtends { } - ToCheckExtends<? super B> pr; + ToCheckExtends<? super B> pr; } void testS3() { @@ -164,6 +166,152 @@ public class WithingBounds { class ToCheckExtends { } - ToCheckExtends<? super A> pr; + ToCheckExtends<? super A> pr; } + + void testMisc() { + class A {} + class i {} + final class ii extends i {} + + A pr4; + AString> pr5; + A pr51; + A? extends String> pr52; + A pr53; + Ai> pr54; + A pr55; + A pr56; + A pr57; + A pr58; + A? super i> pr59; + A? extends A> pr510; + Aii[]> pr511; + A pr512; + + A pr30; + Ai> pr3; + A pr330; + A pr2; + A? super String> pr10; + AA> pr31; + Aii[]> pr32; + AA> pr33; + + A pr6; + Ai> pr6x1; + A pr6x2; + A pr6x3; + AInteger> pr8; + + A? extends String> pr12; + A pr13; + A pr14; + A pr13x3; + A pr13x4; + + + A pr19; + A pr110; + A pr11x0; + A? super i> pr111; + + A pr15; + Aii[]> pr16; + AA> pr17; + AA> pr18; + A? super String> pr112; + A' is not within its bound; should extend '? extends ii'">? super A> pr113; + + A pr701; + AString> pr72; + A pr73; + Aii[]> pr74; + A? extends String> pr75; + A pr76; + A pr77; + A pr78; + A pr79; + A pr791; + A pr713; + A? super String> pr712; + A? super i> pr710; + Ai> pr70; + A pr71; + A pr711; + + A a1; + AObject> a2; + A a3; + Ai[]> a4; + + Aint[]> a5; + A a6; + A a7; + A a8; + Aint[]> a9; + A a10; + A a11; + Ai[]> a12; + A a13; + + A a14; + A a140; + Ai[]> a141; + Ai> a142; + AString> a143; + Ai[][]> a144; + A a145; + A a146; + A a147; + A? extends i> a148; + A? extends String> a149; + A a1410; + A? extends i[][]> a1411; + A a1412; + A a1413; + A? super i[]> a1414; + A? super i> a1415; + A? super String> a1416; + A? super i[][]> a1417; + + A a15; + A a150; + Ai[]> a151; + Ai> a152; + AString> a153; + Ai[][]> a154; + A a155; + A a156; + A a157; + A? extends i> a158; + A? extends String> a159; + A a1510; + A? extends i[][]> a1511; + A a1512; + A a1513; + A? super i[]> a1514; + A? super i> a1515; + A? super String> a1516; + A? super i[][]> a1517; + + A a16; + A a160; + A a161; + A a162; + A< I1[], ? extends I[]> cl; + + } + + void testRawTypes() { + class A> {} + A a; + A<String> a1; + A<A> a2; + AA>> a3; + + A a4; + A<? super A> a5; + A<A[]> a7; + } } diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting/WildcardCastConversion.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting/WildcardCastConversion.java index fc0da5e88ac8..500f1549639c 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting/WildcardCastConversion.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting/WildcardCastConversion.java @@ -375,7 +375,7 @@ public class Test { void foo() { D x = null; - D<? extends B> y = (D<? extends B>) x; + D<? extends B> y = (D<? extends B>) x; } } diff --git a/java/openapi/src/com/intellij/psi/PsiWildcardType.java b/java/openapi/src/com/intellij/psi/PsiWildcardType.java index f825fe939bb5..6a1e1a13ab50 100644 --- a/java/openapi/src/com/intellij/psi/PsiWildcardType.java +++ b/java/openapi/src/com/intellij/psi/PsiWildcardType.java @@ -186,6 +186,13 @@ public class PsiWildcardType extends PsiType { return myBound != null && !myIsExtending; } + /** + * @return false for unbounded wildcards, true otherwise + */ + public boolean isBounded() { + return myBound != null; + } + /** * A lower bound that this wildcard imposes on type parameter value.
* That is:
diff --git a/java/openapi/src/com/intellij/psi/util/TypesDistinctProver.java b/java/openapi/src/com/intellij/psi/util/TypesDistinctProver.java index a34cb05b525d..803075eb00d5 100644 --- a/java/openapi/src/com/intellij/psi/util/TypesDistinctProver.java +++ b/java/openapi/src/com/intellij/psi/util/TypesDistinctProver.java @@ -15,7 +15,12 @@ */ package com.intellij.psi.util; +import com.intellij.openapi.project.Project; import com.intellij.psi.*; +import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.util.containers.HashSet; + +import java.util.Set; /** * User: anna @@ -41,7 +46,8 @@ public class TypesDistinctProver { if (((PsiWildcardType)type1).isExtends()) { final PsiType extendsBound = ((PsiWildcardType)type1).getExtendsBound(); - if (extendsBound.getArrayDimensions() > 0) return true; + if (extendsBound instanceof PsiArrayType && + proveArrayTypeDistinct(((PsiWildcardType)type1).getManager().getProject(), (PsiArrayType)extendsBound, type2)) return true; final PsiClass boundClass1 = PsiUtil.resolveClassInType(TypeConversionUtil.erasure(extendsBound)); if (boundClass1 == null) return false; return proveExtendsBoundsDistinct(type1, type2, boundClass1, psiClass2); @@ -49,7 +55,8 @@ public class TypesDistinctProver { if (((PsiWildcardType)type1).isSuper()) { final PsiType superBound = ((PsiWildcardType)type1).getSuperBound(); - if (superBound.getArrayDimensions() > 0) return true; + if (superBound instanceof PsiArrayType && + proveArrayTypeDistinct(((PsiWildcardType)type1).getManager().getProject(), (PsiArrayType)superBound, type2)) return true; final PsiClass boundClass1 = PsiUtil.resolveClassInType(TypeConversionUtil.erasure(superBound)); if (boundClass1 == null || boundClass1 instanceof PsiTypeParameter) return false; return !InheritanceUtil.isInheritorOrSelf(boundClass1, psiClass2, true); @@ -58,6 +65,10 @@ public class TypesDistinctProver { final PsiType bound = ((PsiWildcardType)type1).getBound(); return bound != null && !bound.equals(psiClass2); } + + if (type2 instanceof PsiArrayType) { + return proveArrayTypeDistinct(((PsiWildcardType)type1).getManager().getProject(), (PsiArrayType)type2, type1); + } } if (type1 instanceof PsiCapturedWildcardType) return provablyDistinct(((PsiCapturedWildcardType)type1).getWildcard(), type2); @@ -90,7 +101,8 @@ public class TypesDistinctProver { if (type1.isExtends() && type2.isExtends()) { final PsiType extendsBound1 = type1.getExtendsBound(); final PsiType extendsBound2 = type2.getExtendsBound(); - if (extendsBound1.getArrayDimensions() != extendsBound2.getArrayDimensions()) return true; + if (extendsBound1 instanceof PsiArrayType && proveArrayTypeDistinct(type1.getManager().getProject(), (PsiArrayType)extendsBound1, extendsBound2) || + extendsBound2 instanceof PsiArrayType && proveArrayTypeDistinct(type1.getManager().getProject(), (PsiArrayType)extendsBound2, extendsBound1)) return true; final PsiClass boundClass1 = PsiUtil.resolveClassInType(extendsBound1); final PsiClass boundClass2 = PsiUtil.resolveClassInType(extendsBound2); @@ -103,7 +115,8 @@ public class TypesDistinctProver { if (type1.isExtends() && type2.isSuper()) { final PsiType extendsBound = type1.getExtendsBound(); final PsiType superBound = type2.getSuperBound(); - if (extendsBound.getArrayDimensions() != superBound.getArrayDimensions()) return true; + if (extendsBound instanceof PsiArrayType && proveArrayTypeDistinct(type1.getManager().getProject(), (PsiArrayType)extendsBound, superBound) || + superBound instanceof PsiArrayType && proveArrayTypeDistinct(type1.getManager().getProject(), (PsiArrayType)superBound, extendsBound)) return true; final PsiClass extendsBoundClass = PsiUtil.resolveClassInType(extendsBound); final PsiClass superBoundClass = PsiUtil.resolveClassInType(superBound); @@ -147,4 +160,36 @@ public class TypesDistinctProver { if (types.length == 0) return false; return provablyDistinct(PsiWildcardType.createExtends(typeParameter.getManager(), types[0]), type); } + + public static boolean proveArrayTypeDistinct(Project project, + PsiArrayType type, + PsiType bound) { + final JavaPsiFacade facade = JavaPsiFacade.getInstance(project); + final GlobalSearchScope searchScope = GlobalSearchScope.allScope(project); + final Set possibleClasses = new HashSet(); + possibleClasses.add(facade.findClass(CommonClassNames.JAVA_IO_SERIALIZABLE, searchScope)); + possibleClasses.add(facade.findClass(CommonClassNames.JAVA_LANG_CLONEABLE, searchScope)); + possibleClasses.add(facade.findClass(CommonClassNames.JAVA_LANG_OBJECT, searchScope)); + + if (type.getArrayDimensions() == bound.getArrayDimensions()) { + final PsiType componentType = type.getComponentType(); + final PsiType boundComponentType = ((PsiArrayType)bound).getComponentType(); + if (boundComponentType instanceof PsiClassType && componentType instanceof PsiClassType) { + return proveExtendsBoundsDistinct(boundComponentType, componentType, ((PsiClassType)boundComponentType).resolve(), ((PsiClassType)componentType).resolve()); + } + else { + return !bound.equals(type); + } + } + else if (bound.getArrayDimensions() + 1 == type.getArrayDimensions() && bound.getDeepComponentType() instanceof PsiClassType) { + return !possibleClasses.contains(((PsiClassType)bound.getDeepComponentType()).resolve()); + } + else if (bound.getArrayDimensions() == type.getArrayDimensions() + 1 && type.getDeepComponentType() instanceof PsiClassType) { + return !possibleClasses.contains(((PsiClassType)type.getDeepComponentType()).resolve()); + } + else if (bound instanceof PsiClassType) { + return !possibleClasses.contains(((PsiClassType)bound).resolve()); + } + return true; + } }