mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-05-06 05:10:22 +07:00
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:
@@ -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;
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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() {
|
||||
|
||||
Reference in New Issue
Block a user