least upper bound: pull unbounded wildcard up so types Number & Comparable<? extends Number & Comparable<?>> are produced instead of Number & Comparable<? extends Comparable<?>>

inferred type in bounds check cleanup: accept that inference result is not within its bounds and give the inference the second chance
This commit is contained in:
Anna Kozlova
2015-12-09 11:16:24 +01:00
parent 1402b31623
commit dd38082794
10 changed files with 73 additions and 130 deletions

View File

@@ -44,7 +44,6 @@ import com.intellij.psi.infos.MethodCandidateInfo;
import com.intellij.psi.javadoc.PsiDocComment;
import com.intellij.psi.javadoc.PsiDocTagValue;
import com.intellij.psi.util.*;
import com.intellij.util.ArrayUtil;
import com.intellij.util.ObjectUtils;
import com.intellij.util.containers.MostlySingularMultiMap;
import gnu.trove.THashMap;
@@ -1339,16 +1338,6 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
myHolder.add(HighlightUtil.checkUnhandledExceptions(expression, expression.getTextRange()));
}
if (!myHolder.hasErrorResults() && method instanceof PsiTypeParameterListOwner) {
PsiTypeParameter[] typeParameters = ((PsiTypeParameterListOwner)method).getTypeParameters();
if (method instanceof PsiMethod) {
final PsiClass containingClass = ((PsiMethod)method).getContainingClass();
assert containingClass != null : method;
typeParameters = ArrayUtil.mergeArrays(typeParameters, containingClass.getTypeParameters());
}
myHolder.add(GenericsHighlightUtil.checkInferredTypeArguments(typeParameters, expression, result.getSubstitutor(), true));
}
if (!myHolder.hasErrorResults()) {
if (results.length == 0) {
String description = null;

View File

@@ -109,25 +109,40 @@ public class GenericsUtil {
return PsiType.getJavaLangObject(manager, aClass.getResolveScope());
}
final PsiElementFactory elementFactory = JavaPsiFacade.getInstance(manager.getProject()).getElementFactory();
PsiClassType[] conjuncts = new PsiClassType[supers.length];
for (int i = 0; i < supers.length; i++) {
PsiClass aSuper = supers[i];
PsiSubstitutor subst1 = TypeConversionUtil.getSuperClassSubstitutor(aSuper, aClass, classResolveResult1.getSubstitutor());
PsiSubstitutor subst2 = TypeConversionUtil.getSuperClassSubstitutor(aSuper, bClass, classResolveResult2.getSubstitutor());
PsiSubstitutor substitutor = PsiSubstitutor.EMPTY;
final Couple<PsiType> types = Couple.<PsiType>of(elementFactory.createType(aSuper, subst1), elementFactory.createType(aSuper, subst2));
for (PsiTypeParameter parameter : PsiUtil.typeParametersIterable(aSuper)) {
PsiType mapping1 = subst1.substitute(parameter);
PsiType mapping2 = subst2.substitute(parameter);
if (mapping1 != null && mapping2 != null) {
substitutor = substitutor.put(parameter, getLeastContainingTypeArgument(mapping1, mapping2, compared, manager, type1.equals(mapping1) && type2.equals(mapping2) ? aSuper : null, parameter));
if (compared.contains(types)) {
substitutor = substitutor.put(parameter, PsiWildcardType.createUnbounded(manager));
}
else {
compared.add(types);
try {
substitutor = substitutor.put(parameter, getLeastContainingTypeArgument(mapping1, mapping2, compared, manager));
}
finally {
compared.remove(types);
}
}
}
else {
substitutor = substitutor.put(parameter, null);
}
}
conjuncts[i] = JavaPsiFacade.getInstance(manager.getProject()).getElementFactory().createType(aSuper, substitutor);
conjuncts[i] = elementFactory.createType(aSuper, substitutor);
}
return PsiIntersectionType.createIntersection(conjuncts);
@@ -150,60 +165,37 @@ public class GenericsUtil {
private static PsiType getLeastContainingTypeArgument(PsiType type1,
PsiType type2,
Set<Couple<PsiType>> compared,
PsiManager manager,
PsiClass nestedLayer,
PsiTypeParameter parameter) {
Couple<PsiType> types = Couple.of(type1, type2);
if (compared.contains(types)) {
if (nestedLayer != null) {
PsiSubstitutor subst = PsiSubstitutor.EMPTY;
for (PsiTypeParameter param : PsiUtil.typeParametersIterable(nestedLayer)) {
subst = subst.put(param, PsiWildcardType.createUnbounded(manager));
}
subst = subst.put(parameter, getLeastContainingTypeArgument(type1, type2, compared, manager, null, null));
final PsiClassType boundType = JavaPsiFacade.getInstance(manager.getProject()).getElementFactory().createType(nestedLayer, subst);
return PsiWildcardType.createExtends(manager, boundType);
}
return PsiWildcardType.createUnbounded(manager);
}
compared.add(types);
try {
if (type1 instanceof PsiWildcardType) {
PsiWildcardType wild1 = (PsiWildcardType)type1;
final PsiType bound1 = wild1.getBound();
if (bound1 == null) return type1;
if (type2 instanceof PsiWildcardType) {
PsiWildcardType wild2 = (PsiWildcardType)type2;
final PsiType bound2 = wild2.getBound();
if (bound2 == null) return type2;
if (wild1.isExtends() == wild2.isExtends()) {
return wild1.isExtends() ?
PsiWildcardType.createExtends(manager, getLeastUpperBound(bound1, bound2, compared, manager)) :
PsiWildcardType.createSuper(manager, getGreatestLowerBound(bound1, bound2));
}
else {
return bound1.equals(bound2) ? bound1 : PsiWildcardType.createUnbounded(manager);
}
PsiManager manager) {
if (type1 instanceof PsiWildcardType) {
PsiWildcardType wild1 = (PsiWildcardType)type1;
final PsiType bound1 = wild1.getBound();
if (bound1 == null) return type1;
if (type2 instanceof PsiWildcardType) {
PsiWildcardType wild2 = (PsiWildcardType)type2;
final PsiType bound2 = wild2.getBound();
if (bound2 == null) return type2;
if (wild1.isExtends() == wild2.isExtends()) {
return wild1.isExtends() ?
PsiWildcardType.createExtends(manager, getLeastUpperBound(bound1, bound2, compared, manager)) :
PsiWildcardType.createSuper(manager, getGreatestLowerBound(bound1, bound2));
}
else {
return wild1.isExtends() ? PsiWildcardType.createExtends(manager, getLeastUpperBound(bound1, type2, compared, manager)) :
wild1.isSuper() ? PsiWildcardType.createSuper(manager, getGreatestLowerBound(bound1, type2)) :
wild1;
return bound1.equals(bound2) ? bound1 : PsiWildcardType.createUnbounded(manager);
}
}
else if (type2 instanceof PsiWildcardType) {
return getLeastContainingTypeArgument(type2, type1, compared, manager, null, null);
else {
return wild1.isExtends() ? PsiWildcardType.createExtends(manager, getLeastUpperBound(bound1, type2, compared, manager)) :
wild1.isSuper() ? PsiWildcardType.createSuper(manager, getGreatestLowerBound(bound1, type2)) :
wild1;
}
//Done with wildcards
}
else if (type2 instanceof PsiWildcardType) {
return getLeastContainingTypeArgument(type2, type1, compared, manager);
}
//Done with wildcards
if (type1.equals(type2)) return type1;
return PsiWildcardType.createExtends(manager, getLeastUpperBound(type1, type2, compared, manager));
}
finally {
compared.remove(types);
}
if (type1.equals(type2)) return type1;
return PsiWildcardType.createExtends(manager, getLeastUpperBound(type1, type2, compared, manager));
}
@NotNull
@@ -283,76 +275,16 @@ public class GenericsUtil {
}
}
//todo process type parameter bounds
for (PsiType type : extendsTypes) {
PsiType extendsType = substitutor.substitute(type);
if (substituted instanceof PsiWildcardType) {
final PsiType extendsBound = ((PsiWildcardType)substituted).getExtendsBound();
if (acceptExtendsBound(extendsType, extendsBound)) {
return null;
}
}
else if (substituted instanceof PsiIntersectionType) {
for (PsiType extendsBound : ((PsiIntersectionType)substituted).getConjuncts()) {
if (acceptExtendsBound(extendsType, extendsBound)) return null;
}
}
else if (substituted instanceof PsiCapturedWildcardType) {
final PsiType extendsBound = ((PsiCapturedWildcardType)substituted).getUpperBound();
if (acceptExtendsBound(extendsType, extendsBound) || extendsType.equals(extendsBound)) {
return null;
}
}
if (extendsType != null &&
!TypeConversionUtil.isAssignable(extendsType, substituted, allowUncheckedConversion) &&
!TypeConversionUtil.isAssignable(type, substituted, allowUncheckedConversion)) {
!TypeConversionUtil.isAssignable(extendsType, substituted, allowUncheckedConversion)) {
return extendsType;
}
}
return null;
}
public static boolean acceptExtendsBound(PsiType extendsType, PsiType extendsBound) {
if (Comparing.equal(TypeConversionUtil.erasure(extendsType), TypeConversionUtil.erasure(extendsBound))) {
if (extendsBound instanceof PsiClassType) {
if (acceptExtendsBound((PsiClassType)extendsBound, 0)) return true;
}
else if (extendsBound instanceof PsiIntersectionType) {
for (PsiType psiType : ((PsiIntersectionType)extendsBound).getConjuncts()) {
if (psiType instanceof PsiClassType) {
if (acceptExtendsBound((PsiClassType)psiType, 0)) return true;
}
}
}
}
return false;
}
private static boolean acceptExtendsBound(PsiClassType extendsBound, int depth) {
PsiType[] parameters = extendsBound.getParameters();
if (parameters.length == 1) {
PsiType argType = parameters[0];
if (argType instanceof PsiCapturedWildcardType && depth == 0) {
argType = ((PsiCapturedWildcardType)argType).getWildcard();
}
if (argType instanceof PsiWildcardType) {
if (!((PsiWildcardType)argType).isBounded()) return true;
final PsiType bound = ((PsiWildcardType)argType).getExtendsBound();
if (bound instanceof PsiClassType && TypeConversionUtil.erasure(bound).equals(TypeConversionUtil.erasure(extendsBound))) {
return acceptExtendsBound((PsiClassType)bound, depth + 1);
}
if (bound instanceof PsiIntersectionType) {
for (PsiType extendsType : ((PsiIntersectionType)bound).getConjuncts()) {
if (acceptExtendsBound(extendsBound, extendsType)) {
return true;
}
}
}
}
}
return false;
}
public static boolean isFromExternalTypeLanguage(@NotNull PsiType type) {
String internalCanonicalText = type.getInternalCanonicalText();
return internalCanonicalText != null && internalCanonicalText.equals(type.getCanonicalText());

View File

@@ -14,4 +14,13 @@ abstract class A1<T>{
void bar(A1<Long> x, A1<Integer> y){
baz(foo(x, y));
}
}
}
abstract class A3<T>{
abstract <S> S foo(S x, S y);
<S extends Number & Comparable<? extends K>, K extends Number & Comparable<? extends M>, M extends Number & Comparable<?>> void baz(A<S> a){}
void bar(A<Long> x, A<Integer> y){
baz(foo(x, y));
}
}

View File

@@ -8,7 +8,7 @@ class Test {
}
void foo() {
<error descr="Incompatible types. Found: 'java.util.List<java.lang.Class<? extends java.io.Serializable & java.lang.Comparable<? extends java.lang.Comparable<?>>>>', required: 'java.util.List<java.lang.Class<? extends java.io.Serializable>>'">List<Class<? extends Serializable>> l = <warning descr="Unchecked generics array creation for varargs parameter">this.asList</warning>(String.class, Integer.class);</error>
<error descr="Incompatible types. Found: 'java.util.List<java.lang.Class<? extends java.io.Serializable & java.lang.Comparable<? extends java.io.Serializable & java.lang.Comparable<?>>>>', required: 'java.util.List<java.lang.Class<? extends java.io.Serializable>>'">List<Class<? extends Serializable>> l = <warning descr="Unchecked generics array creation for varargs parameter">this.asList</warning>(String.class, Integer.class);</error>
l.size();
List<? extends Object> objects = this.asList(new String(), new Integer(0));
objects.size();
@@ -146,7 +146,7 @@ class IDEADEV25515 {
}
public static final
<error descr="Incompatible types. Found: 'java.util.List<java.lang.Class<? extends java.io.Serializable & java.lang.Comparable<? extends java.lang.Comparable<?>>>>', required: 'java.util.List<java.lang.Class<? extends java.io.Serializable>>'">List<Class<? extends Serializable>> SIMPLE_TYPES =
<error descr="Incompatible types. Found: 'java.util.List<java.lang.Class<? extends java.io.Serializable & java.lang.Comparable<? extends java.io.Serializable & java.lang.Comparable<?>>>>', required: 'java.util.List<java.lang.Class<? extends java.io.Serializable>>'">List<Class<? extends Serializable>> SIMPLE_TYPES =
<warning descr="Unchecked generics array creation for varargs parameter">asList</warning>(String.class, Integer.class ,Long.class, Double.class, /*Date.class,*/
Boolean.class, Boolean.TYPE /*,String[].class */ /*,BigDecimal.class*/);</error>

View File

@@ -1,6 +1,6 @@
class Test {
void bar() {
baz<error descr="'baz(S)' in 'Test' cannot be applied to '(java.io.Serializable & java.lang.Comparable<? extends java.io.Serializable & java.lang.Comparable<? extends java.lang.Comparable<?>>>)'">(foo(1, ""))</error>;
baz<error descr="'baz(S)' in 'Test' cannot be applied to '(java.io.Serializable & java.lang.Comparable<? extends java.io.Serializable & java.lang.Comparable<?>>)'">(foo(1, ""))</error>;
}
<T> T foo(T x, T y) {

View File

@@ -1,6 +1,6 @@
import java.io.Serializable;
// "Change type arguments to <Serializable & Comparable<? extends Serializable & Comparable<? extends Comparable<?>>>>" "true"
// "Change type arguments to <Serializable & Comparable<? extends Serializable & Comparable<?>>>" "true"
class Generic<E> {
Generic(E arg, E arg1) {
}

View File

@@ -1,4 +1,4 @@
// "Change type arguments to <Serializable & Comparable<? extends Serializable & Comparable<? extends Comparable<?>>>>" "true"
// "Change type arguments to <Serializable & Comparable<? extends Serializable & Comparable<?>>>" "true"
class Generic<E> {
Generic(E arg, E arg1) {
}

View File

@@ -8,6 +8,6 @@ class Test {
enum Y implements I {a}
{
List<? extends Enum<? extends Enum<? extends Enum<?>>>> l = Arrays.asList(X.a, Y.a);
List<? extends Enum<? extends Enum<?>>> l = Arrays.asList(X.a, Y.a);
}
}

View File

@@ -24,7 +24,10 @@ import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.roots.LanguageLevelProjectExtension;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.GenericsUtil;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiManager;
import com.intellij.psi.PsiType;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.testFramework.IdeaTestUtil;
import org.jetbrains.annotations.NonNls;
@@ -540,6 +543,16 @@ public class GenericsHighlightingTest extends LightDaemonAnalyzerTestCase {
doTest(LanguageLevel.JDK_1_7, JavaSdkVersion.JDK_1_7, false);
}
public void testLeastUpperBoundWithRecursiveTypes() throws Exception {
final PsiManager manager = getPsiManager();
final GlobalSearchScope scope = GlobalSearchScope.allScope(getProject());
final PsiType leastUpperBound = GenericsUtil.getLeastUpperBound(PsiType.INT.getBoxedType(manager, scope),
PsiType.LONG.getBoxedType(manager, scope),
manager);
assertNotNull(leastUpperBound);
assertEquals("Number & Comparable<? extends Number & Comparable<?>>", leastUpperBound.getPresentableText());
}
public void testJavaUtilCollections_NoVerify() throws Exception {
PsiClass collectionsClass = getJavaFacade().findClass("java.util.Collections", GlobalSearchScope.moduleWithLibrariesScope(getModule()));
assertNotNull(collectionsClass);

View File

@@ -450,7 +450,7 @@ public class IntroduceVariableTest extends LightCodeInsightTestCase {
}
public void testIntersectionWildcardExpectedType() {
doTest(new MockIntroduceVariableHandler("l", false, false, false, "java.util.List<? extends java.lang.Enum<? extends java.lang.Enum<? extends java.lang.Enum<?>>>>", true));
doTest(new MockIntroduceVariableHandler("l", false, false, false, "java.util.List<? extends java.lang.Enum<? extends java.lang.Enum<?>>>", true));
}
public void testMethodRefNotInContextInferredFilterWithNonAcceptableSince() {