captured wildcards: refactor to extract same parts for extends/super wildcards, ensure upper bound is not lost for extends wildcard (IDEA-57292)

This commit is contained in:
Anna Kozlova
2015-05-21 10:00:23 +02:00
parent d31fb8b83f
commit fea58865be
7 changed files with 84 additions and 66 deletions
@@ -297,7 +297,7 @@ public class GenericsUtil {
}
else if (substituted instanceof PsiCapturedWildcardType) {
final PsiType extendsBound = ((PsiCapturedWildcardType)substituted).getUpperBound();
if (acceptExtendsBound(extendsType, extendsBound) || extendsType.equals(substitutor.substitute(extendsBound))) {
if (acceptExtendsBound(extendsType, extendsBound) || extendsType.equals(extendsBound)) {
return null;
}
}
@@ -49,21 +49,6 @@ public class PsiCapturedWildcardType extends PsiType.Stub {
myExistential = existential;
myContext = context;
myParameter = parameter;
if (parameter != null) {
final PsiClassType[] boundTypes = parameter.getExtendsListTypes();
if (boundTypes.length > 0) {
PsiType result = null;
for (PsiType type : boundTypes) {
if (result == null) {
result = type;
}
else {
result = GenericsUtil.getGreatestLowerBound(result, type);
}
}
myUpperBound = result;
}
}
}
@Override
@@ -719,16 +719,22 @@ public final class PsiUtil extends PsiUtilCore {
final PsiClass aClass = result.getElement();
if (aClass != null) {
final PsiSubstitutor substitutor = result.getSubstitutor();
PsiSubstitutor captureSubstitutor = substitutor;
for (PsiTypeParameter typeParameter : typeParametersIterable(aClass)) {
final PsiType substituted = substitutor.substitute(typeParameter);
if (substituted instanceof PsiWildcardType) {
captureSubstitutor = captureSubstitutor.put(typeParameter, PsiCapturedWildcardType.create((PsiWildcardType)substituted, context, typeParameter));
}
}
Map<PsiTypeParameter, PsiType> substitutionMap = null;
for (PsiTypeParameter typeParameter : typeParametersIterable(aClass)) {
final PsiType substituted = substitutor.substitute(typeParameter);
if (substituted instanceof PsiWildcardType) {
if (substitutionMap == null) substitutionMap = new HashMap<PsiTypeParameter, PsiType>(substitutor.getSubstitutionMap());
substitutionMap.put(typeParameter,
captureTypeParameterBounds(typeParameter, substituted, context,
substitutor.put(typeParameter,
PsiCapturedWildcardType
.create((PsiWildcardType)substituted, context, typeParameter))));
captureTypeParameterBounds(typeParameter, substituted, context, captureSubstitutor));
}
}
@@ -748,7 +754,7 @@ public final class PsiUtil extends PsiUtilCore {
public static PsiType captureTypeParameterBounds(@NotNull PsiTypeParameter typeParameter, PsiType substituted,
PsiElement context,
PsiSubstitutor substitutor) {
PsiSubstitutor captureSubstitutor) {
PsiType oldSubstituted = substituted;
PsiElement captureContext = context;
if (substituted instanceof PsiCapturedWildcardType) {
@@ -756,66 +762,63 @@ public final class PsiUtil extends PsiUtilCore {
substituted = captured.getWildcard();
captureContext = captured.getContext();
}
if (substituted instanceof PsiWildcardType && !((PsiWildcardType)substituted).isSuper()) {
PsiType originalBound = ((PsiWildcardType)substituted).getBound();
PsiManager manager = typeParameter.getManager();
PsiType glb = null;
if (substituted instanceof PsiWildcardType) {
final PsiType[] boundTypes = typeParameter.getExtendsListTypes();
PsiManager manager = typeParameter.getManager();
PsiType originalBound = !((PsiWildcardType)substituted).isSuper() ? ((PsiWildcardType)substituted).getBound() : null;
glb = originalBound;
for (PsiType boundType : boundTypes) {
PsiType substitutedBoundType = substitutor.substitute(boundType);
PsiWildcardType wildcardType = (PsiWildcardType)substituted;
PsiType substitutedBoundType = captureSubstitutor.substitute(boundType);
if (substitutedBoundType != null && !(substitutedBoundType instanceof PsiWildcardType) &&
!substitutedBoundType.equalsToText(CommonClassNames.JAVA_LANG_OBJECT)) {
if (originalBound instanceof PsiArrayType &&
substitutedBoundType instanceof PsiArrayType &&
!originalBound.isAssignableFrom(substitutedBoundType) &&
!substitutedBoundType.isAssignableFrom(originalBound)) {
continue;
}
if (originalBound == null ||
!TypeConversionUtil.erasure(substitutedBoundType).isAssignableFrom(TypeConversionUtil.erasure(originalBound)) &&
!TypeConversionUtil.erasure(substitutedBoundType).isAssignableFrom(originalBound)) { //erasure is essential to avoid infinite recursion
if (wildcardType.isExtends()) {
final PsiType bound = wildcardType.getBound();
if (bound instanceof PsiArrayType && substitutedBoundType instanceof PsiArrayType &&
!bound.isAssignableFrom(substitutedBoundType) && !substitutedBoundType.isAssignableFrom(bound)) {
continue;
}
final PsiType glb = GenericsUtil.getGreatestLowerBound(bound, substitutedBoundType);
if (glb != null) {
substituted = PsiWildcardType.createExtends(manager, glb);
}
if (glb == null) {
glb = substitutedBoundType;
}
else {
//unbounded
substituted = substitutedBoundType instanceof PsiCapturedWildcardType
? ((PsiCapturedWildcardType)substitutedBoundType).getWildcard()
: PsiWildcardType.createExtends(manager, substitutedBoundType);
glb = GenericsUtil.getGreatestLowerBound(glb, substitutedBoundType);
}
}
}
}
} else if (substituted instanceof PsiWildcardType && ((PsiWildcardType)substituted).isSuper()) {
final PsiType[] boundTypes = typeParameter.getExtendsListTypes();
PsiType glb = null;
for (PsiType boundType : boundTypes) {
final PsiType substitutedBound = substitutor.substitute(boundType);
if (substitutedBound != null) {
if (glb == null) {
glb = substitutedBound;
}
else {
glb = GenericsUtil.getGreatestLowerBound(glb, substitutedBound);
if (glb != null) {
if (!((PsiWildcardType)substituted).isSuper()) {
substituted = glb instanceof PsiCapturedWildcardType ? ((PsiCapturedWildcardType)glb).getWildcard()
: PsiWildcardType.createExtends(manager, glb);
}
else {
if (captureContext != null) {
final PsiCapturedWildcardType capturedWildcardType = oldSubstituted instanceof PsiCapturedWildcardType
? (PsiCapturedWildcardType)oldSubstituted
: (PsiCapturedWildcardType)captureSubstitutor.substitute(typeParameter);
LOG.assertTrue(capturedWildcardType != null);
capturedWildcardType.setUpperBound(glb);
return capturedWildcardType;
}
}
}
if (glb != null && captureContext != null) {
final PsiCapturedWildcardType capturedWildcardType = oldSubstituted instanceof PsiCapturedWildcardType
? (PsiCapturedWildcardType)oldSubstituted
: PsiCapturedWildcardType.create((PsiWildcardType)substituted, captureContext, typeParameter);
capturedWildcardType.setUpperBound(glb);
return capturedWildcardType;
}
}
if (captureContext != null) {
LOG.assertTrue(substituted instanceof PsiWildcardType, substituted);
substituted = oldSubstituted instanceof PsiCapturedWildcardType && substituted == ((PsiCapturedWildcardType)oldSubstituted).getWildcard()
substituted = oldSubstituted instanceof PsiCapturedWildcardType && substituted.equals(((PsiCapturedWildcardType)oldSubstituted).getWildcard())
? oldSubstituted
: PsiCapturedWildcardType.create((PsiWildcardType)substituted, captureContext, typeParameter);
if (glb != null) {
((PsiCapturedWildcardType)substituted).setUpperBound(glb);
}
}
return substituted;
}
@@ -67,7 +67,14 @@ public abstract class PsiTypeMapper extends PsiTypeVisitorEx<PsiType> {
@Override
public PsiType visitCapturedWildcardType(final PsiCapturedWildcardType type) {
PsiWildcardType mapped = mapType(type.getWildcard());
return mapped == null ? null : PsiCapturedWildcardType.create(mapped, type.getContext(), type.getTypeParameter());
if (mapped == null) {
return null;
}
else {
final PsiCapturedWildcardType capturedWildcardType = PsiCapturedWildcardType.create(mapped, type.getContext(), type.getTypeParameter());
capturedWildcardType.setUpperBound(mapType(type.getUpperBound()));
return capturedWildcardType;
}
}
@Override
@@ -423,12 +423,7 @@ public class PsiImplUtil {
return wildcardType.isSuper() ? wildcardType.getBound() : PsiCapturedWildcardType.create(wildcardType, expression);
}
else {
if (wildcardType.isExtends()) {
return wildcardType.getBound();
}
else {
return ((PsiCapturedWildcardType)type).getUpperBound();
}
return ((PsiCapturedWildcardType)type).getUpperBound();
}
}
@@ -0,0 +1,24 @@
class C56 {
class A<T,S extends T> {}
class C {
void foo(A<?,?> x){
bar(x);
}
<T,S extends T> void bar(A<T,S> x){}
}
}
class C57 {
class B<T,S> {}
class A<T,S extends T> extends B<T,S> {}
class C {
void foo(A<?,?> x){
bar(x);
}
<T,S extends T> void bar(B<T,S> x){}
}
}
@@ -496,6 +496,10 @@ public class GenericsHighlightingTest extends LightDaemonAnalyzerTestCase {
doTest(LanguageLevel.JDK_1_8, JavaSdkVersion.JDK_1_8, true);
}
public void testPreserveCaptureWildcardsInUpperBounds() throws Exception {
doTest(LanguageLevel.JDK_1_7, JavaSdkVersion.JDK_1_7, false);
}
public void testJavaUtilCollections_NoVerify() throws Exception {
PsiClass collectionsClass = getJavaFacade().findClass("java.util.Collections", GlobalSearchScope.moduleWithLibrariesScope(getModule()));
assertNotNull(collectionsClass);