assignment: stop bounds promotion inside nested wildcards (IDEA-139161)

This commit is contained in:
Anna Kozlova
2015-12-09 18:49:28 +01:00
parent c66efb2458
commit 92487fe273
5 changed files with 46 additions and 21 deletions
@@ -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
@@ -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();
@@ -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<Boolean>() {
@NotNull
@Override
public Boolean compute() {
return isAssignable(typeRight, leftBound, false);
return isAssignable(typeRight, leftBound, false, false);
}
});
return assignable == null || assignable.booleanValue();
@@ -0,0 +1,8 @@
interface A<T extends B<? extends T>> { }
interface B<T extends A<?>> { }
class C {
void foo(A<?> x){
<error descr="Incompatible types. Found: 'A<capture<?>>', required: 'A<? extends B<? extends A<?>>>'">A<? extends B<? extends A<?>>> y = x;</error>
}
}
@@ -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);
}