diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java index 14203591b3a8..ce8720593f3c 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java @@ -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; diff --git a/java/java-psi-api/src/com/intellij/psi/GenericsUtil.java b/java/java-psi-api/src/com/intellij/psi/GenericsUtil.java index e9f57671aaeb..53c3aa129b02 100644 --- a/java/java-psi-api/src/com/intellij/psi/GenericsUtil.java +++ b/java/java-psi-api/src/com/intellij/psi/GenericsUtil.java @@ -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 types = Couple.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> compared, - PsiManager manager, - PsiClass nestedLayer, - PsiTypeParameter parameter) { - Couple 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()); diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting/IDEA57492.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting/IDEA57492.java index 5cd402a50c26..b46d90f7729f 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting/IDEA57492.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting/IDEA57492.java @@ -14,4 +14,13 @@ abstract class A1{ void bar(A1 x, A1 y){ baz(foo(x, y)); } -} \ No newline at end of file +} + +abstract class A3{ + abstract S foo(S x, S y); + , K extends Number & Comparable, M extends Number & Comparable> void baz(A a){} + + void bar(A x, A y){ + baz(foo(x, y)); + } +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting/IntersectionTypes.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting/IntersectionTypes.java index 5706e9f12618..868efce3432a 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting/IntersectionTypes.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting/IntersectionTypes.java @@ -8,7 +8,7 @@ class Test { } void foo() { - List> l = this.asList(String.class, Integer.class); + List> l = this.asList(String.class, Integer.class); l.size(); List objects = this.asList(new String(), new Integer(0)); objects.size(); @@ -146,7 +146,7 @@ class IDEADEV25515 { } public static final - List> SIMPLE_TYPES = + List> SIMPLE_TYPES = asList(String.class, Integer.class ,Long.class, Double.class, /*Date.class,*/ Boolean.class, Boolean.TYPE /*,String[].class */ /*,BigDecimal.class*/); diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting8/IDEA139222.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting8/IDEA139222.java index e6e4e01930ef..c6523a15ca85 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting8/IDEA139222.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting8/IDEA139222.java @@ -1,6 +1,6 @@ class Test { void bar() { - baz(foo(1, "")); + baz(foo(1, "")); } T foo(T x, T y) { diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeTypeArgs/afterSuperType.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeTypeArgs/afterSuperType.java index 85e4d69968a2..a3682b815f0f 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeTypeArgs/afterSuperType.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeTypeArgs/afterSuperType.java @@ -1,6 +1,6 @@ import java.io.Serializable; -// "Change type arguments to >>>" "true" +// "Change type arguments to >>" "true" class Generic { Generic(E arg, E arg1) { } diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeTypeArgs/beforeSuperType.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeTypeArgs/beforeSuperType.java index 782196410955..348aa8536943 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeTypeArgs/beforeSuperType.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeTypeArgs/beforeSuperType.java @@ -1,4 +1,4 @@ -// "Change type arguments to >>>" "true" +// "Change type arguments to >>" "true" class Generic { Generic(E arg, E arg1) { } diff --git a/java/java-tests/testData/refactoring/introduceVariable/IntersectionWildcardExpectedType.after.java b/java/java-tests/testData/refactoring/introduceVariable/IntersectionWildcardExpectedType.after.java index 1248f2cdfc6e..e0f2293938ba 100644 --- a/java/java-tests/testData/refactoring/introduceVariable/IntersectionWildcardExpectedType.after.java +++ b/java/java-tests/testData/refactoring/introduceVariable/IntersectionWildcardExpectedType.after.java @@ -8,6 +8,6 @@ class Test { enum Y implements I {a} { - List>>> l = Arrays.asList(X.a, Y.a); + List>> l = Arrays.asList(X.a, Y.a); } } diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/GenericsHighlightingTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/GenericsHighlightingTest.java index 22f93eb08f53..0004a3aa866d 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/GenericsHighlightingTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/GenericsHighlightingTest.java @@ -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>", leastUpperBound.getPresentableText()); + } + public void testJavaUtilCollections_NoVerify() throws Exception { PsiClass collectionsClass = getJavaFacade().findClass("java.util.Collections", GlobalSearchScope.moduleWithLibrariesScope(getModule())); assertNotNull(collectionsClass); diff --git a/java/java-tests/testSrc/com/intellij/refactoring/IntroduceVariableTest.java b/java/java-tests/testSrc/com/intellij/refactoring/IntroduceVariableTest.java index 61a6442f400d..604e779a9544 100644 --- a/java/java-tests/testSrc/com/intellij/refactoring/IntroduceVariableTest.java +++ b/java/java-tests/testSrc/com/intellij/refactoring/IntroduceVariableTest.java @@ -450,7 +450,7 @@ public class IntroduceVariableTest extends LightCodeInsightTestCase { } public void testIntersectionWildcardExpectedType() { - doTest(new MockIntroduceVariableHandler("l", false, false, false, "java.util.List>>>", true)); + doTest(new MockIntroduceVariableHandler("l", false, false, false, "java.util.List>>", true)); } public void testMethodRefNotInContextInferredFilterWithNonAcceptableSince() {