new inference: well formed check

This commit is contained in:
Anna Kozlova
2014-02-06 19:34:15 +01:00
parent d24e983826
commit ff66c1b01a
4 changed files with 95 additions and 80 deletions
@@ -234,7 +234,7 @@ public class GenericsHighlightUtil {
final PsiClass referenceClass = type instanceof PsiClassType ? ((PsiClassType)type).resolve() : null;
final PsiType psiType = substitutor.substitute(classParameter);
if (psiType instanceof PsiClassType && !(PsiUtil.resolveClassInType(psiType) instanceof PsiTypeParameter)) {
if (checkNotInBounds(type, psiType, referenceParameterList)) {
if (GenericsUtil.checkNotInBounds(type, psiType, referenceParameterList)) {
final String description = "Actual type argument and inferred type contradict each other";
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(typeElement2Highlight).descriptionAndTooltip(description).create();
}
@@ -243,7 +243,7 @@ public class GenericsHighlightUtil {
final PsiClassType[] bounds = classParameter.getSuperTypes();
for (PsiClassType type1 : bounds) {
PsiType bound = substitutor.substitute(type1);
if (!bound.equalsToText(CommonClassNames.JAVA_LANG_OBJECT) && checkNotInBounds(type, bound, referenceParameterList)) {
if (!bound.equalsToText(CommonClassNames.JAVA_LANG_OBJECT) && GenericsUtil.checkNotInBounds(type, bound, referenceParameterList)) {
PsiClass boundClass = bound instanceof PsiClassType ? ((PsiClassType)bound).resolve() : null;
@NonNls final String messageKey = boundClass == null || referenceClass == null || referenceClass.isInterface() == boundClass.isInterface()
@@ -267,79 +267,6 @@ public class GenericsHighlightUtil {
return null;
}
private static boolean checkNotInBounds(PsiType type, PsiType bound, PsiReferenceParameterList referenceParameterList) {
if (type instanceof PsiClassType) {
return checkNotAssignable(bound, type, allowUncheckedConversions((PsiClassType)type, referenceParameterList));
}
if (type instanceof PsiWildcardType) {
if (((PsiWildcardType)type).isExtends()) {
return checkExtendsWildcardCaptureFailure((PsiWildcardType)type, bound);
}
else if (((PsiWildcardType)type).isSuper()) {
final PsiType superBound = ((PsiWildcardType)type).getSuperBound();
if (PsiUtil.resolveClassInType(superBound) instanceof PsiTypeParameter) return TypesDistinctProver.provablyDistinct(type, bound);
return checkNotAssignable(bound, superBound, false);
}
}
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,
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(PsiClassType type, PsiReferenceParameterList referenceParameterList) {
final PsiClass psiClass = type.resolve();
if (psiClass != null) {
for (PsiTypeParameter parameter : PsiUtil.typeParametersIterable(psiClass)) {
if (parameter.getExtendsListTypes().length != 0) {
return false;
}
}
if (psiClass instanceof PsiTypeParameter && psiClass.getExtendsListTypes().length != 0) return false;
}
if (!type.isRaw()) return true;
//allow unchecked conversions in method calls but not in type declaration
return referenceParameterList.getParent() instanceof PsiReferenceExpression;
}
private static String typeParameterListOwnerDescription(final PsiTypeParameterListOwner typeParameterListOwner) {
if (typeParameterListOwner instanceof PsiClass) {
return HighlightUtil.formatClass((PsiClass)typeParameterListOwner);
@@ -23,6 +23,7 @@ import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.util.InheritanceUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.psi.util.TypeConversionUtil;
import com.intellij.psi.util.TypesDistinctProver;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.HashMap;
import org.jetbrains.annotations.NotNull;
@@ -495,4 +496,81 @@ public class GenericsUtil {
}
return type;
}
public static boolean checkNotInBounds(PsiType type, PsiType bound, PsiReferenceParameterList referenceParameterList) {
//allow unchecked conversions in method calls but not in type declaration
return checkNotInBounds(type, bound, referenceParameterList.getParent() instanceof PsiReferenceExpression);
}
public static boolean checkNotInBounds(PsiType type, PsiType bound, boolean uncheckedConversionByDefault) {
if (type instanceof PsiClassType) {
return checkNotAssignable(bound, type, allowUncheckedConversions((PsiClassType)type, uncheckedConversionByDefault));
}
if (type instanceof PsiWildcardType) {
if (((PsiWildcardType)type).isExtends()) {
return checkExtendsWildcardCaptureFailure((PsiWildcardType)type, bound);
}
else if (((PsiWildcardType)type).isSuper()) {
final PsiType superBound = ((PsiWildcardType)type).getSuperBound();
if (PsiUtil.resolveClassInType(superBound) instanceof PsiTypeParameter) return TypesDistinctProver.provablyDistinct(type, bound);
return checkNotAssignable(bound, superBound, false);
}
}
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,
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(PsiClassType type, boolean uncheckedConversionByDefault) {
final PsiClass psiClass = type.resolve();
if (psiClass != null) {
for (PsiTypeParameter parameter : PsiUtil.typeParametersIterable(psiClass)) {
if (parameter.getExtendsListTypes().length != 0) {
return false;
}
}
if (psiClass instanceof PsiTypeParameter && psiClass.getExtendsListTypes().length != 0) return false;
}
if (!type.isRaw()) return true;
return uncheckedConversionByDefault;
}
}
@@ -102,8 +102,7 @@ public class FunctionalInterfaceParameterizationUtil {
return null;
}
final PsiSubstitutor substitutor = session.resolveBounds(true);
session.resolveBounds(false);
final PsiType[] newTypeParameters = new PsiType[parameters.length];
for (int i = 0; i < typeParameters.length; i++) {
PsiTypeParameter typeParameter = typeParameters[i];
@@ -118,8 +117,7 @@ public class FunctionalInterfaceParameterizationUtil {
final PsiClassType parameterization = elementFactory.createType(psiClass, newTypeParameters);
if (//todo !TypeConversionUtil.isAssignable(psiClassType, parameterization) ||
!GenericsUtil.isTypeArgumentsApplicable(typeParameters, PsiSubstitutor.EMPTY.putAll(psiClass, newTypeParameters), expr)) {
if (!isWellFormed(psiClass, typeParameters, newTypeParameters)) {
return null;
}
@@ -132,6 +130,18 @@ public class FunctionalInterfaceParameterizationUtil {
return null;
}
private static boolean isWellFormed(PsiClass psiClass, PsiTypeParameter[] typeParameters, PsiType[] newTypeParameters) {
final PsiSubstitutor substitutor = PsiSubstitutor.EMPTY.putAll(psiClass, newTypeParameters);
for (int i = 0; i < typeParameters.length; i++) {
for (PsiClassType bound : typeParameters[i].getExtendsListTypes()) {
if (GenericsUtil.checkNotInBounds(newTypeParameters[i], substitutor.substitute(bound), false)) {
return false;
}
}
}
return true;
}
/**
The function type of a parameterized functional interface, F<A1...An>, where one or more of A1...An is a wildcard, is the function type of the non-wildcard parameterization of F, F<T1...Tn> determined as follows.
Let P1, ..., Pn be the type parameters of F and B1, ..., Bn be the corresponding bounds. For all i, 1 i n, Ti is derived according to the form of Ai:
@@ -8,7 +8,7 @@ class Test {
void foo(I<? extends String, ? extends List<? extends String>> fip) { }
void test() {
foo(<error descr="Cannot infer functional interface type">(ArrayList<? extends String> p) -> p.get(0)</error>);
foo<error descr="'foo(Test.I<? extends java.lang.String,? extends java.util.List<? extends java.lang.String>>)' in 'Test' cannot be applied to '(<lambda expression>)'">((ArrayList<? extends String> p) -> p.get(0))</error>;
}
}