diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/load/java/structure/impl/JavaClassifierTypeImpl.java b/compiler/frontend.java/src/org/jetbrains/kotlin/load/java/structure/impl/JavaClassifierTypeImpl.java index 27420dd9fae..1b7972e57b8 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/load/java/structure/impl/JavaClassifierTypeImpl.java +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/load/java/structure/impl/JavaClassifierTypeImpl.java @@ -17,7 +17,6 @@ package org.jetbrains.kotlin.load.java.structure.impl; import com.intellij.psi.*; -import com.intellij.psi.util.PsiUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.kotlin.load.java.structure.*; @@ -113,8 +112,8 @@ public class JavaClassifierTypeImpl extends JavaTypeImpl implement // parameters including ones from outer class Iterable parameters = classifier instanceof JavaClassImpl - ? getReversedTypeParameters((JavaClassImpl) classifier) - : Collections.emptyList(); + ? getTypeParameters(((JavaClassImpl) classifier).getPsi()) + : Collections.emptyList(); JavaTypeSubstitutor substitutor = getSubstitutor(); @@ -126,16 +125,32 @@ public class JavaClassifierTypeImpl extends JavaTypeImpl implement return result; } - private static Collection getReversedTypeParameters(@NotNull JavaClassImpl classifier) { - Iterable parameters = PsiUtil.typeParametersIterable(classifier.getPsi()); - List result = new ArrayList(); + // Copy-pasted from PsiUtil.typeParametersIterable + // The only change is using `Collections.addAll(result, typeParameters)` instead of reversing type parameters of `currentOwner` + // Result differs in cases like: + // class Outer

{ + // class Inner {} + // } + // + // PsiUtil.typeParametersIterable returns H3, H2, H1 + // But we would like to have H2, H3, H1 as such order is consistent with our type representation + @NotNull + public static List getTypeParameters(@NotNull PsiClass owner) { + List result = null; - for (PsiTypeParameter parameter : parameters) { - result.add(parameter); + PsiTypeParameterListOwner currentOwner = owner; + while (currentOwner != null) { + PsiTypeParameter[] typeParameters = currentOwner.getTypeParameters(); + if (typeParameters.length > 0) { + if (result == null) result = new ArrayList(typeParameters.length); + Collections.addAll(result, typeParameters); + } + + if (currentOwner.hasModifierProperty(PsiModifier.STATIC)) break; + currentOwner = currentOwner.getContainingClass(); } - Collections.reverse(result); - + if (result == null) return Collections.emptyList(); return result; } } diff --git a/compiler/testData/diagnostics/tests/generics/innerClasses/j+k.txt b/compiler/testData/diagnostics/tests/generics/innerClasses/j+k.txt index bd167d4b43e..74394f6bd39 100644 --- a/compiler/testData/diagnostics/tests/generics/innerClasses/j+k.txt +++ b/compiler/testData/diagnostics/tests/generics/innerClasses/j+k.txt @@ -7,7 +7,7 @@ public open class Outer { public/*package*/ open fun baz(): Outer.Inner! public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int - public/*package*/ open fun set(/*0*/ x: Outer.Inner!): kotlin.Unit + public/*package*/ open fun set(/*0*/ x: Outer.Inner!): kotlin.Unit public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String public open inner class Inner /*captured type parameters: /*1*/ E : kotlin.Any!*/ { diff --git a/compiler/testData/diagnostics/tests/generics/innerClasses/j+k_complex.kt b/compiler/testData/diagnostics/tests/generics/innerClasses/j+k_complex.kt new file mode 100644 index 00000000000..2f3016a3c90 --- /dev/null +++ b/compiler/testData/diagnostics/tests/generics/innerClasses/j+k_complex.kt @@ -0,0 +1,35 @@ +// !CHECK_TYPE +// FILE: BaseOuter.java +// See KT-10285 +public class BaseOuter { + abstract public class BaseInner { + public H foo1() { + return null; + } + + public E foo2() { + return null; + } + + public F foo3() { + return null; + } + } +} + +// FILE: Outer.java +public class Outer extends BaseOuter { + public BaseInner bar() { return null; } + public class Inner extends BaseOuter.BaseInner {} +} + +// FILE: main.kt +fun foo(x1: Outer, x2: Outer.Inner) { + x1.bar().foo1().checkType { _() } + x1.bar().foo2().checkType { _() } + x1.bar().foo3().checkType { _() } + + x2.foo1().checkType { _() } + x2.foo2().checkType { _() } + x2.foo3().checkType { _() } +} diff --git a/compiler/testData/diagnostics/tests/generics/innerClasses/j+k_complex.txt b/compiler/testData/diagnostics/tests/generics/innerClasses/j+k_complex.txt new file mode 100644 index 00000000000..1ca202cb922 --- /dev/null +++ b/compiler/testData/diagnostics/tests/generics/innerClasses/j+k_complex.txt @@ -0,0 +1,38 @@ +package + +public fun foo(/*0*/ x1: Outer, /*1*/ x2: Outer.Inner): kotlin.Unit + +public open class BaseOuter { + public constructor BaseOuter() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + + public abstract inner class BaseInner /*captured type parameters: /*2*/ H : kotlin.Any!*/ { + public constructor BaseInner() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open fun foo1(): H! + public open fun foo2(): E! + public open fun foo3(): F! + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } +} + +public open class Outer : BaseOuter { + public constructor Outer() + public open fun bar(): BaseOuter.BaseInner! + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + + public open inner class Inner /*captured type parameters: /*0*/ H : kotlin.Any!*/ : BaseOuter.BaseInner { + public constructor Inner() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun foo1(): H! + public open override /*1*/ /*fake_override*/ fun foo2(): kotlin.Double! + public open override /*1*/ /*fake_override*/ fun foo3(): kotlin.String! + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } +} diff --git a/compiler/testData/loadJava/compiledJava/InnerClassTypeMultipleGeneric.java b/compiler/testData/loadJava/compiledJava/InnerClassTypeMultipleGeneric.java new file mode 100644 index 00000000000..75a3b278ae4 --- /dev/null +++ b/compiler/testData/loadJava/compiledJava/InnerClassTypeMultipleGeneric.java @@ -0,0 +1,16 @@ +package test; +public class InnerClassTypeMultipleGeneric { + public class BaseOuter { + abstract public class BaseInner { + } + } + + public class Outer extends BaseOuter { + public BaseInner, CharSequence> bar() { return null; } + public class Inner extends BaseOuter.BaseInner {} + } + + public Outer.Inner staticType() { + return null; + } +} diff --git a/compiler/testData/loadJava/compiledJava/InnerClassTypeMultipleGeneric.txt b/compiler/testData/loadJava/compiledJava/InnerClassTypeMultipleGeneric.txt new file mode 100644 index 00000000000..4700f0291af --- /dev/null +++ b/compiler/testData/loadJava/compiledJava/InnerClassTypeMultipleGeneric.txt @@ -0,0 +1,23 @@ +package test + +public open class InnerClassTypeMultipleGeneric { + public constructor InnerClassTypeMultipleGeneric() + public open fun staticType(): test.InnerClassTypeMultipleGeneric.Outer.Inner! + + public open inner class BaseOuter { + public constructor BaseOuter() + + public abstract inner class BaseInner /*captured type parameters: /*2*/ H1 : kotlin.Any!, /*3*/ H2 : kotlin.Any!*/ { + public constructor BaseInner() + } + } + + public open inner class Outer : test.InnerClassTypeMultipleGeneric.BaseOuter { + public constructor Outer() + public open fun bar(): test.InnerClassTypeMultipleGeneric.BaseOuter.BaseInner!, kotlin.CharSequence!>! + + public open inner class Inner /*captured type parameters: /*1*/ E1 : kotlin.Any!, /*2*/ E2 : kotlin.Any!*/ : test.InnerClassTypeMultipleGeneric.BaseOuter.BaseInner { + public constructor Inner() + } + } +} diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java index e45e9abbbe4..c36a50ce7af 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -6797,6 +6797,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { doTest(fileName); } + @TestMetadata("j+k_complex.kt") + public void testJ_k_complex() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/generics/innerClasses/j+k_complex.kt"); + doTest(fileName); + } + @TestMetadata("kt408.kt") public void testKt408() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/generics/innerClasses/kt408.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/jvm/compiler/LoadJavaTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/jvm/compiler/LoadJavaTestGenerated.java index c198dd600bd..21557c88014 100644 --- a/compiler/tests/org/jetbrains/kotlin/jvm/compiler/LoadJavaTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/jvm/compiler/LoadJavaTestGenerated.java @@ -133,6 +133,12 @@ public class LoadJavaTestGenerated extends AbstractLoadJavaTest { doTestCompiledJava(fileName); } + @TestMetadata("InnerClassTypeMultipleGeneric.java") + public void testInnerClassTypeMultipleGeneric() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/loadJava/compiledJava/InnerClassTypeMultipleGeneric.java"); + doTestCompiledJava(fileName); + } + @TestMetadata("InnerClassesInGeneric.java") public void testInnerClassesInGeneric() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/loadJava/compiledJava/InnerClassesInGeneric.java"); diff --git a/compiler/tests/org/jetbrains/kotlin/jvm/runtime/JvmRuntimeDescriptorLoaderTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/jvm/runtime/JvmRuntimeDescriptorLoaderTestGenerated.java index 338fd3fa174..2598edc9b41 100644 --- a/compiler/tests/org/jetbrains/kotlin/jvm/runtime/JvmRuntimeDescriptorLoaderTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/jvm/runtime/JvmRuntimeDescriptorLoaderTestGenerated.java @@ -3199,6 +3199,12 @@ public class JvmRuntimeDescriptorLoaderTestGenerated extends AbstractJvmRuntimeD doTest(fileName); } + @TestMetadata("InnerClassTypeMultipleGeneric.java") + public void testInnerClassTypeMultipleGeneric() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/loadJava/compiledJava/InnerClassTypeMultipleGeneric.java"); + doTest(fileName); + } + @TestMetadata("InnerClassesInGeneric.java") public void testInnerClassesInGeneric() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/loadJava/compiledJava/InnerClassesInGeneric.java");