mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
assignment: stop bounds promotion inside nested wildcards (IDEA-139161)
This commit is contained in:
@@ -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();
|
||||
|
||||
+8
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user