recursive types capture: avoid capture comparison during glb computation (IDEA-57379; IDEA-139167; IDEA-139157)

This commit is contained in:
Anna Kozlova
2015-12-04 12:56:44 +01:00
parent c9285488b7
commit f55279387c
10 changed files with 83 additions and 27 deletions

View File

@@ -55,7 +55,11 @@ public class PsiCapturedWildcardType extends PsiType.Stub {
myUpperBound = PsiType.getJavaLangObject(myContext.getManager(), getResolveScope());
}
private static RecursionGuard guard = RecursionManager.createGuard("captureGuard");
public static RecursionGuard guard = RecursionManager.createGuard("captureGuard");
public static boolean isNoCapture() {
return !guard.currentStack().isEmpty();
}
@Override
public boolean equals(Object o) {
@@ -86,7 +90,7 @@ public class PsiCapturedWildcardType extends PsiType.Stub {
}
});
if (sameUpperBounds != null && sameUpperBounds) {
if (sameUpperBounds == null || sameUpperBounds) {
return true;
}
}

View File

@@ -15,6 +15,7 @@
*/
package com.intellij.psi;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.util.PsiUtil;
@@ -58,10 +59,18 @@ public class PsiIntersectionType extends PsiType.Stub {
return new PsiIntersectionType(conjuncts);
}
private static PsiType[] flattenAndRemoveDuplicates(PsiType[] conjuncts) {
private static PsiType[] flattenAndRemoveDuplicates(final PsiType[] conjuncts) {
try {
Set<PsiType> flattened = flatten(conjuncts, ContainerUtil.<PsiType>newLinkedHashSet());
return flattened.toArray(createArray(flattened.size()));
final Set<PsiType> flattenConjuncts = PsiCapturedWildcardType.guard.doPreventingRecursion(conjuncts, true, new Computable<Set<PsiType>>() {
@Override
public Set<PsiType> compute() {
return flatten(conjuncts, ContainerUtil.<PsiType>newLinkedHashSet());
}
});
if (flattenConjuncts == null) {
return conjuncts;
}
return flattenConjuncts.toArray(createArray(flattenConjuncts.size()));
}
catch (NoSuchElementException e) {
throw new RuntimeException(Arrays.toString(conjuncts), e);

View File

@@ -609,8 +609,8 @@ 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 = (PsiClassType)captureToplevelWildcards(thisClassType, aClass);
final PsiClassType capture2 = (PsiClassType)captureToplevelWildcards(otherClassType, bClass);
final PsiClassType capture1 = PsiCapturedWildcardType.isNoCapture() ? thisClassType : (PsiClassType)captureToplevelWildcards(thisClassType, aClass);
final PsiClassType capture2 = PsiCapturedWildcardType.isNoCapture() ? otherClassType : (PsiClassType)captureToplevelWildcards(otherClassType, bClass);
final PsiClassType.ClassResolveResult result1 = capture1.resolveGenerics();
final PsiClassType.ClassResolveResult result2 = capture2.resolveGenerics();
@@ -806,7 +806,7 @@ public final class PsiUtil extends PsiUtilCore {
PsiType originalBound = !((PsiWildcardType)substituted).isSuper() ? ((PsiWildcardType)substituted).getBound() : null;
glb = originalBound;
for (PsiType boundType : boundTypes) {
PsiType substitutedBoundType = captureSubstitutor.substitute(boundType);
final PsiType substitutedBoundType = captureSubstitutor.substitute(boundType);
if (substitutedBoundType != null && !(substitutedBoundType instanceof PsiWildcardType) &&
!substitutedBoundType.equalsToText(CommonClassNames.JAVA_LANG_OBJECT)) {
if (originalBound instanceof PsiArrayType &&
@@ -816,16 +816,11 @@ public final class PsiUtil extends PsiUtilCore {
continue;
}
if (originalBound == null ||
!TypeConversionUtil.erasure(substitutedBoundType).isAssignableFrom(TypeConversionUtil.erasure(originalBound)) &&
!TypeConversionUtil.erasure(substitutedBoundType).isAssignableFrom(originalBound)) { //erasure is essential to avoid infinite recursion
if (glb == null) {
glb = substitutedBoundType;
}
else {
glb = GenericsUtil.getGreatestLowerBound(glb, substitutedBoundType);
}
if (glb == null) {
glb = substitutedBoundType;
}
else {
glb = GenericsUtil.getGreatestLowerBound(glb, substitutedBoundType);
}
}
}

View File

@@ -958,7 +958,8 @@ public class TypeConversionUtil {
PsiTypeParameter rp = ri.next();
final PsiType typeLeft = leftSubstitutor.substitute(lp);
if (typeLeft == null) continue;
final PsiType typeRight = rightSubstitutor.substituteWithBoundsPromotion(rp);
final PsiType typeRight = PsiCapturedWildcardType.isNoCapture() ? rightSubstitutor.substitute(rp)
: rightSubstitutor.substituteWithBoundsPromotion(rp);
if (typeRight == null) {
// compatibility feature: allow to assign raw types to generic ones
return allowUncheckedConversion;

View File

@@ -52,9 +52,9 @@ class Test {
}*/
abstract class D<T extends Throwable & Runnable> {
<error descr="'foo(T, D<? extends Runnable>)' clashes with 'foo(T, D<? extends Throwable>)'; both methods have same erasure">abstract <T extends Serializable & Comparable<?>> void foo(T x, D<? extends Runnable> y)</error>;
<error descr="'foo(T, D<? extends Runnable>)' is already defined in 'Test.D'">abstract <T extends Serializable & Comparable<?>> void foo(T x, D<? extends Runnable> y)</error>;
abstract <T extends Serializable & Comparable<?>> void foo(T x, D<? extends Throwable> y);
<error descr="'foo(T, D<? extends Throwable>)' is already defined in 'Test.D'">abstract <T extends Serializable & Comparable<?>> void foo(T x, D<? extends Throwable> y)</error>;
}
@@ -62,8 +62,8 @@ class Test {
interface IB {}
void testExtendsOrder() {
class E<T extends IA & IB> {
<error descr="'foo(E<? extends IA>)' clashes with 'foo(E<? extends IB>)'; both methods have same erasure">void foo(E<? extends IA> x)</error> {}
void foo(E<? extends IB> x) {}
<error descr="'foo(E<? extends IA>)' is already defined in 'E'">void foo(E<? extends IA> x)</error> {}
<error descr="'foo(E<? extends IB>)' is already defined in 'E'">void foo(E<? extends IB> x)</error> {}
}
}

View File

@@ -52,9 +52,9 @@ class Test {
}*/
abstract class D<T extends Throwable & Runnable> {
<error descr="'foo(T, D<? extends Runnable>)' clashes with 'foo(T, D<? extends Throwable>)'; both methods have same erasure">abstract <T extends Serializable & Comparable<?>> void foo(T x, D<? extends Runnable> y)</error>;
<error descr="'foo(T, D<? extends Runnable>)' is already defined in 'Test.D'">abstract <T extends Serializable & Comparable<?>> void foo(T x, D<? extends Runnable> y)</error>;
abstract <T extends Serializable & Comparable<?>> void foo(T x, D<? extends Throwable> y);
<error descr="'foo(T, D<? extends Throwable>)' is already defined in 'Test.D'">abstract <T extends Serializable & Comparable<?>> void foo(T x, D<? extends Throwable> y)</error>;
}
@@ -62,8 +62,8 @@ class Test {
interface IB {}
void testExtendsOrder() {
class E<T extends IA & IB> {
<error descr="'foo(E<? extends IA>)' clashes with 'foo(E<? extends IB>)'; both methods have same erasure">void foo(E<? extends IA> x)</error> {}
void foo(E<? extends IB> x) {}
<error descr="'foo(E<? extends IA>)' is already defined in 'E'">void foo(E<? extends IA> x)</error> {}
<error descr="'foo(E<? extends IB>)' is already defined in 'E'">void foo(E<? extends IB> x)</error> {}
}
}

View File

@@ -0,0 +1,12 @@
class A<T extends Iterable<?>>{
T get() {return null;}
T x;
void foo(A<? extends Iterable<String> > a){
String s = bar(a.get());
String s1 = bar(a.x);
}
<Tb> Tb bar(Iterable<Tb> a){
return null;
}
}

View File

@@ -0,0 +1,10 @@
import java.util.*;
interface A<T extends A<? extends A<T>>> { }
class C {
void foo(List<A<? extends A<?>>> x){
List<A<?>> y = x;
}
}

View File

@@ -0,0 +1,13 @@
import java.util.List;
interface A<T extends List<String>> {
T m();
}
abstract class C {
abstract <S> A<? extends S> foo();
void bar() {
this.<List<?>>foo().m().get(0).toLowerCase();
}
}

View File

@@ -922,4 +922,16 @@ public class GenericsHighlighting8Test extends LightDaemonAnalyzerTestCase {
public void testDistinguishTypeArgs() throws Exception {
doTest();
}
public void testRecursiveCapturedWildcardTypes() throws Exception {
doTest();
}
public void testRecursiveCapturedWildcardTypesIDEA139167() throws Exception {
doTest();
}
public void testRecursiveCapturedWildcardTypesIDEA139157() throws Exception {
doTest();
}
}