Support declarations returning object literals in ultra-light classes
This commit is contained in:
@@ -10,6 +10,7 @@ import com.intellij.psi.PsiElement;
|
|||||||
import kotlin.Pair;
|
import kotlin.Pair;
|
||||||
import kotlin.Unit;
|
import kotlin.Unit;
|
||||||
import kotlin.collections.CollectionsKt;
|
import kotlin.collections.CollectionsKt;
|
||||||
|
import kotlin.jvm.functions.Function1;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.jetbrains.kotlin.builtins.BuiltInsPackageFragment;
|
import org.jetbrains.kotlin.builtins.BuiltInsPackageFragment;
|
||||||
@@ -89,6 +90,8 @@ public class KotlinTypeMapper {
|
|||||||
private final LanguageVersionSettings languageVersionSettings;
|
private final LanguageVersionSettings languageVersionSettings;
|
||||||
private final boolean isReleaseCoroutines;
|
private final boolean isReleaseCoroutines;
|
||||||
private final boolean isIrBackend;
|
private final boolean isIrBackend;
|
||||||
|
@Nullable
|
||||||
|
private final Function1<KotlinType, KotlinType> typePreprocessor;
|
||||||
|
|
||||||
private final TypeMappingConfiguration<Type> typeMappingConfiguration = new TypeMappingConfiguration<Type>() {
|
private final TypeMappingConfiguration<Type> typeMappingConfiguration = new TypeMappingConfiguration<Type>() {
|
||||||
@NotNull
|
@NotNull
|
||||||
@@ -121,6 +124,13 @@ public class KotlinTypeMapper {
|
|||||||
public boolean releaseCoroutines() {
|
public boolean releaseCoroutines() {
|
||||||
return isReleaseCoroutines;
|
return isReleaseCoroutines;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public KotlinType preprocessType(@NotNull KotlinType kotlinType) {
|
||||||
|
if (typePreprocessor == null) return null;
|
||||||
|
return typePreprocessor.invoke(kotlinType);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private static final TypeMappingConfiguration<Type> staticTypeMappingConfiguration = new TypeMappingConfiguration<Type>() {
|
private static final TypeMappingConfiguration<Type> staticTypeMappingConfiguration = new TypeMappingConfiguration<Type>() {
|
||||||
@@ -151,8 +161,35 @@ public class KotlinTypeMapper {
|
|||||||
public boolean releaseCoroutines() {
|
public boolean releaseCoroutines() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public KotlinType preprocessType(@NotNull KotlinType kotlinType) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
public KotlinTypeMapper(
|
||||||
|
@NotNull BindingContext bindingContext,
|
||||||
|
@NotNull ClassBuilderMode classBuilderMode,
|
||||||
|
@NotNull IncompatibleClassTracker incompatibleClassTracker,
|
||||||
|
@NotNull String moduleName,
|
||||||
|
@NotNull JvmTarget jvmTarget,
|
||||||
|
@NotNull LanguageVersionSettings languageVersionSettings,
|
||||||
|
boolean isIrBackend,
|
||||||
|
@Nullable Function1<KotlinType, KotlinType> typePreprocessor
|
||||||
|
) {
|
||||||
|
this.bindingContext = bindingContext;
|
||||||
|
this.classBuilderMode = classBuilderMode;
|
||||||
|
this.incompatibleClassTracker = incompatibleClassTracker;
|
||||||
|
this.moduleName = moduleName;
|
||||||
|
this.jvmTarget = jvmTarget;
|
||||||
|
this.languageVersionSettings = languageVersionSettings;
|
||||||
|
this.isReleaseCoroutines = languageVersionSettings.supportsFeature(LanguageFeature.ReleaseCoroutines);
|
||||||
|
this.isIrBackend = isIrBackend;
|
||||||
|
this.typePreprocessor = typePreprocessor;
|
||||||
|
}
|
||||||
|
|
||||||
public KotlinTypeMapper(
|
public KotlinTypeMapper(
|
||||||
@NotNull BindingContext bindingContext,
|
@NotNull BindingContext bindingContext,
|
||||||
@NotNull ClassBuilderMode classBuilderMode,
|
@NotNull ClassBuilderMode classBuilderMode,
|
||||||
@@ -162,14 +199,7 @@ public class KotlinTypeMapper {
|
|||||||
@NotNull LanguageVersionSettings languageVersionSettings,
|
@NotNull LanguageVersionSettings languageVersionSettings,
|
||||||
boolean isIrBackend
|
boolean isIrBackend
|
||||||
) {
|
) {
|
||||||
this.bindingContext = bindingContext;
|
this(bindingContext, classBuilderMode, incompatibleClassTracker, moduleName, jvmTarget, languageVersionSettings, isIrBackend, null);
|
||||||
this.classBuilderMode = classBuilderMode;
|
|
||||||
this.incompatibleClassTracker = incompatibleClassTracker;
|
|
||||||
this.moduleName = moduleName;
|
|
||||||
this.jvmTarget = jvmTarget;
|
|
||||||
this.languageVersionSettings = languageVersionSettings;
|
|
||||||
this.isReleaseCoroutines = languageVersionSettings.supportsFeature(LanguageFeature.ReleaseCoroutines);
|
|
||||||
this.isIrBackend = isIrBackend;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -21,13 +21,18 @@ import org.jetbrains.kotlin.codegen.state.IncompatibleClassTracker
|
|||||||
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
|
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
|
||||||
import org.jetbrains.kotlin.config.JvmTarget
|
import org.jetbrains.kotlin.config.JvmTarget
|
||||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||||
|
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
|
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.ValueDescriptor
|
import org.jetbrains.kotlin.descriptors.ValueDescriptor
|
||||||
import org.jetbrains.kotlin.load.kotlin.TypeMappingMode
|
import org.jetbrains.kotlin.load.kotlin.TypeMappingMode
|
||||||
import org.jetbrains.kotlin.psi.KtDeclaration
|
import org.jetbrains.kotlin.psi.KtDeclaration
|
||||||
import org.jetbrains.kotlin.psi.KtTypeParameterListOwner
|
import org.jetbrains.kotlin.psi.KtTypeParameterListOwner
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext
|
import org.jetbrains.kotlin.resolve.BindingContext
|
||||||
|
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||||
import org.jetbrains.kotlin.types.KotlinType
|
import org.jetbrains.kotlin.types.KotlinType
|
||||||
|
import org.jetbrains.kotlin.types.TypeProjectionImpl
|
||||||
|
import org.jetbrains.kotlin.types.replace
|
||||||
|
import org.jetbrains.kotlin.types.typeUtil.supertypes
|
||||||
import java.text.StringCharacterIterator
|
import java.text.StringCharacterIterator
|
||||||
|
|
||||||
internal fun buildTypeParameterList(
|
internal fun buildTypeParameterList(
|
||||||
@@ -105,5 +110,38 @@ internal fun typeMapper(support: UltraLightSupport): KotlinTypeMapper = KotlinTy
|
|||||||
IncompatibleClassTracker.DoNothing, support.moduleName,
|
IncompatibleClassTracker.DoNothing, support.moduleName,
|
||||||
JvmTarget.JVM_1_8,
|
JvmTarget.JVM_1_8,
|
||||||
KotlinTypeMapper.LANGUAGE_VERSION_SETTINGS_DEFAULT, // TODO use proper LanguageVersionSettings
|
KotlinTypeMapper.LANGUAGE_VERSION_SETTINGS_DEFAULT, // TODO use proper LanguageVersionSettings
|
||||||
false
|
false,
|
||||||
|
KotlinType::cleanFromAnonymousTypes
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Returns null when type is unchanged
|
||||||
|
fun KotlinType.cleanFromAnonymousTypes(): KotlinType? {
|
||||||
|
val returnTypeClass = constructor.declarationDescriptor as? ClassDescriptor ?: return null
|
||||||
|
if (DescriptorUtils.isAnonymousObject(returnTypeClass)) {
|
||||||
|
// We choose just the first supertype because:
|
||||||
|
// - In public declarations, object literals should always have a single supertype (otherwise it's an error)
|
||||||
|
// - For private declarations, they might have more than one supertype
|
||||||
|
// but it looks like it's not important how we choose a representative for them
|
||||||
|
val representative = returnTypeClass.defaultType.supertypes().firstOrNull() ?: return null
|
||||||
|
return representative.cleanFromAnonymousTypes() ?: representative
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arguments.isEmpty()) return null
|
||||||
|
|
||||||
|
var wasUpdates = false
|
||||||
|
|
||||||
|
val newArguments = arguments.map { typeProjection ->
|
||||||
|
val updatedType =
|
||||||
|
typeProjection.takeUnless { it.isStarProjection }
|
||||||
|
?.type?.cleanFromAnonymousTypes()
|
||||||
|
?: return@map typeProjection
|
||||||
|
|
||||||
|
wasUpdates = true
|
||||||
|
|
||||||
|
TypeProjectionImpl(typeProjection.projectionKind, updatedType)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!wasUpdates) return null
|
||||||
|
|
||||||
|
return replace(newArguments = newArguments)
|
||||||
|
}
|
||||||
|
|||||||
+101
@@ -0,0 +1,101 @@
|
|||||||
|
public final class Prop /* Prop*/ {
|
||||||
|
@org.jetbrains.annotations.NotNull()
|
||||||
|
private final java.lang.Object someProp;
|
||||||
|
|
||||||
|
@null()
|
||||||
|
public Prop();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class Fun /* Fun*/ {
|
||||||
|
@null()
|
||||||
|
public Fun();
|
||||||
|
|
||||||
|
@org.jetbrains.annotations.NotNull()
|
||||||
|
private final java.lang.Object someFun();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class ArrayOfAnonymous /* ArrayOfAnonymous*/ {
|
||||||
|
@org.jetbrains.annotations.NotNull()
|
||||||
|
private final java.lang.Object[] a1;
|
||||||
|
|
||||||
|
@null()
|
||||||
|
public ArrayOfAnonymous();
|
||||||
|
|
||||||
|
@org.jetbrains.annotations.NotNull()
|
||||||
|
public final java.lang.Object[] getA1();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
final class C /* C*/ {
|
||||||
|
@null()
|
||||||
|
private final int y;
|
||||||
|
|
||||||
|
@org.jetbrains.annotations.NotNull()
|
||||||
|
private final kotlin.jvm.functions.Function0<java.lang.Object> initChild;
|
||||||
|
|
||||||
|
@null()
|
||||||
|
public C(@null() int);
|
||||||
|
|
||||||
|
@null()
|
||||||
|
public final int getY();
|
||||||
|
|
||||||
|
@org.jetbrains.annotations.NotNull()
|
||||||
|
public final kotlin.jvm.functions.Function0<java.lang.Object> getInitChild();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract class Super /* Super*/ {
|
||||||
|
@null()
|
||||||
|
public Super();
|
||||||
|
|
||||||
|
@org.jetbrains.annotations.Nullable()
|
||||||
|
public abstract java.lang.Object getA();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class Sub /* Sub*/ extends Super {
|
||||||
|
@org.jetbrains.annotations.NotNull()
|
||||||
|
private final java.lang.Object[] a;
|
||||||
|
|
||||||
|
@null()
|
||||||
|
public Sub();
|
||||||
|
|
||||||
|
@org.jetbrains.annotations.NotNull()
|
||||||
|
public java.lang.Object[] getA();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class ValidPublicSupertype /* ValidPublicSupertype*/ {
|
||||||
|
@org.jetbrains.annotations.NotNull()
|
||||||
|
private final java.lang.Runnable x;
|
||||||
|
|
||||||
|
@null()
|
||||||
|
public ValidPublicSupertype();
|
||||||
|
|
||||||
|
@org.jetbrains.annotations.NotNull()
|
||||||
|
public final java.lang.Runnable bar();
|
||||||
|
|
||||||
|
@org.jetbrains.annotations.NotNull()
|
||||||
|
public final java.lang.Runnable getX();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract interface I /* I*/ {
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class InvalidPublicSupertype /* InvalidPublicSupertype*/ {
|
||||||
|
@org.jetbrains.annotations.NotNull()
|
||||||
|
private final java.lang.Runnable x;
|
||||||
|
|
||||||
|
@null()
|
||||||
|
public InvalidPublicSupertype();
|
||||||
|
|
||||||
|
@org.jetbrains.annotations.NotNull()
|
||||||
|
public final java.lang.Runnable bar();
|
||||||
|
|
||||||
|
@org.jetbrains.annotations.NotNull()
|
||||||
|
public final java.lang.Runnable getX();
|
||||||
|
|
||||||
|
}
|
||||||
+29
-10
@@ -1,22 +1,21 @@
|
|||||||
/** should load cls */
|
|
||||||
class Prop {
|
class Prop {
|
||||||
private val someProp = object { }
|
private val someProp = object { }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/** should load cls */
|
|
||||||
class Fun {
|
class Fun {
|
||||||
private fun someFun() = object { }
|
private fun someFun() = object { }
|
||||||
}
|
}
|
||||||
|
|
||||||
/** should load cls */
|
|
||||||
class Array {
|
class ArrayOfAnonymous {
|
||||||
val a1 = arrayOf(
|
val a1 = arrayOf(
|
||||||
object { val fy = "text"}
|
object { val fy = "text"}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** should load cls */
|
|
||||||
private class C(val y: Int) {
|
private class C(val y: Int) {
|
||||||
val initChild = { ->
|
val initChild = { ->
|
||||||
object {
|
object {
|
||||||
@@ -28,14 +27,34 @@ private class C(val y: Int) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class Super {
|
abstract class Super {
|
||||||
val a: Any?
|
abstract val a: Any?
|
||||||
}
|
}
|
||||||
|
|
||||||
/** should load cls */
|
|
||||||
class Sub {
|
class Sub : Super() {
|
||||||
override val a = arrayOf(
|
override val a = arrayOf(
|
||||||
object { val fy = "text"}
|
object { val fy = "text"}
|
||||||
)
|
)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
class ValidPublicSupertype {
|
||||||
|
val x = object : Runnable {
|
||||||
|
override fun run() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun bar() = object : Runnable {
|
||||||
|
override fun run() {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface I
|
||||||
|
class InvalidPublicSupertype {
|
||||||
|
val x = object : Runnable, I {
|
||||||
|
override fun run() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun bar() = object : Runnable, I {
|
||||||
|
override fun run() {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -40,6 +40,9 @@ interface TypeMappingConfiguration<out T : Any> {
|
|||||||
fun getPredefinedTypeForClass(classDescriptor: ClassDescriptor): T?
|
fun getPredefinedTypeForClass(classDescriptor: ClassDescriptor): T?
|
||||||
fun getPredefinedInternalNameForClass(classDescriptor: ClassDescriptor): String?
|
fun getPredefinedInternalNameForClass(classDescriptor: ClassDescriptor): String?
|
||||||
fun processErrorType(kotlinType: KotlinType, descriptor: ClassDescriptor)
|
fun processErrorType(kotlinType: KotlinType, descriptor: ClassDescriptor)
|
||||||
|
// returns null when type doesn't need to be preprocessed
|
||||||
|
fun preprocessType(kotlinType: KotlinType): KotlinType? = null
|
||||||
|
|
||||||
fun releaseCoroutines(): Boolean
|
fun releaseCoroutines(): Boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,6 +57,10 @@ fun <T : Any> mapType(
|
|||||||
writeGenericType: (KotlinType, T, TypeMappingMode) -> Unit = DO_NOTHING_3,
|
writeGenericType: (KotlinType, T, TypeMappingMode) -> Unit = DO_NOTHING_3,
|
||||||
isIrBackend: Boolean
|
isIrBackend: Boolean
|
||||||
): T {
|
): T {
|
||||||
|
typeMappingConfiguration.preprocessType(kotlinType)?.let { newType ->
|
||||||
|
return mapType(newType, factory, mode, typeMappingConfiguration, descriptorTypeWriter, writeGenericType, isIrBackend)
|
||||||
|
}
|
||||||
|
|
||||||
if (kotlinType.isSuspendFunctionType) {
|
if (kotlinType.isSuspendFunctionType) {
|
||||||
return mapType(
|
return mapType(
|
||||||
transformSuspendFunctionToRuntimeFunctionType(kotlinType, typeMappingConfiguration.releaseCoroutines()),
|
transformSuspendFunctionToRuntimeFunctionType(kotlinType, typeMappingConfiguration.releaseCoroutines()),
|
||||||
|
|||||||
-19
@@ -25,7 +25,6 @@ import com.intellij.psi.search.GlobalSearchScope
|
|||||||
import com.intellij.psi.util.CachedValueProvider
|
import com.intellij.psi.util.CachedValueProvider
|
||||||
import com.intellij.psi.util.CachedValuesManager
|
import com.intellij.psi.util.CachedValuesManager
|
||||||
import com.intellij.psi.util.PsiModificationTracker
|
import com.intellij.psi.util.PsiModificationTracker
|
||||||
import com.intellij.psi.util.PsiTreeUtil
|
|
||||||
import com.intellij.util.containers.ConcurrentFactoryMap
|
import com.intellij.util.containers.ConcurrentFactoryMap
|
||||||
import org.jetbrains.kotlin.asJava.LightClassBuilder
|
import org.jetbrains.kotlin.asJava.LightClassBuilder
|
||||||
import org.jetbrains.kotlin.asJava.LightClassGenerationSupport
|
import org.jetbrains.kotlin.asJava.LightClassGenerationSupport
|
||||||
@@ -53,7 +52,6 @@ import org.jetbrains.kotlin.name.FqName
|
|||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.hasExpectModifier
|
import org.jetbrains.kotlin.psi.psiUtil.hasExpectModifier
|
||||||
import org.jetbrains.kotlin.psi.stubs.elements.KtStubElementTypes
|
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext
|
import org.jetbrains.kotlin.resolve.BindingContext
|
||||||
import org.jetbrains.kotlin.resolve.annotations.JVM_STATIC_ANNOTATION_FQ_NAME
|
import org.jetbrains.kotlin.resolve.annotations.JVM_STATIC_ANNOTATION_FQ_NAME
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
||||||
@@ -157,9 +155,6 @@ class IDELightClassGenerationSupport(private val project: Project) : LightClassG
|
|||||||
}
|
}
|
||||||
if (declaration is KtCallableDeclaration) {
|
if (declaration is KtCallableDeclaration) {
|
||||||
declaration.valueParameters.mapNotNull { findTooComplexDeclaration(it) }.firstOrNull()?.let { return it }
|
declaration.valueParameters.mapNotNull { findTooComplexDeclaration(it) }.firstOrNull()?.let { return it }
|
||||||
if (declaration.typeReference == null && (declaration as? KtFunction)?.hasBlockBody() != true) {
|
|
||||||
findPotentiallyReturnedAnonymousObject(declaration)?.let { return it }
|
|
||||||
}
|
|
||||||
if (declaration.typeReference?.hasModifier(KtTokens.SUSPEND_KEYWORD) == true) {
|
if (declaration.typeReference?.hasModifier(KtTokens.SUSPEND_KEYWORD) == true) {
|
||||||
return declaration.typeReference
|
return declaration.typeReference
|
||||||
}
|
}
|
||||||
@@ -172,19 +167,6 @@ class IDELightClassGenerationSupport(private val project: Project) : LightClassG
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun findPotentiallyReturnedAnonymousObject(declaration: KtDeclaration): KtObjectDeclaration? {
|
|
||||||
val spine = declaration.containingKtFile.stubbedSpine
|
|
||||||
for (i in 0 until spine.stubCount) {
|
|
||||||
if (spine.getStubType(i) == KtStubElementTypes.OBJECT_DECLARATION) {
|
|
||||||
val obj = spine.getStubPsi(i) as KtObjectDeclaration
|
|
||||||
if (obj.isObjectLiteral() && PsiTreeUtil.isContextAncestor(declaration, obj, true)) {
|
|
||||||
return obj
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun implementsKotlinCollection(classOrObject: KtClassOrObject): Boolean {
|
private fun implementsKotlinCollection(classOrObject: KtClassOrObject): Boolean {
|
||||||
if (classOrObject.superTypeListEntries.isEmpty()) return false
|
if (classOrObject.superTypeListEntries.isEmpty()) return false
|
||||||
|
|
||||||
@@ -223,7 +205,6 @@ class IDELightClassGenerationSupport(private val project: Project) : LightClassG
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
override fun createDataHolderForFacade(files: Collection<KtFile>, builder: LightClassBuilder): LightClassDataHolder.ForFacade {
|
override fun createDataHolderForFacade(files: Collection<KtFile>, builder: LightClassBuilder): LightClassDataHolder.ForFacade {
|
||||||
assert(!files.isEmpty()) { "No files in facade" }
|
assert(!files.isEmpty()) { "No files in facade" }
|
||||||
|
|
||||||
|
|||||||
@@ -110,7 +110,7 @@ object UltraLightChecker {
|
|||||||
} +
|
} +
|
||||||
";"
|
";"
|
||||||
|
|
||||||
private fun PsiClass.renderClass(): String {
|
fun PsiClass.renderClass(): String {
|
||||||
val classWord = when {
|
val classWord = when {
|
||||||
isAnnotationType -> "@interface"
|
isAnnotationType -> "@interface"
|
||||||
isInterface -> "interface"
|
isInterface -> "interface"
|
||||||
|
|||||||
+35
-7
@@ -6,10 +6,13 @@
|
|||||||
package org.jetbrains.kotlin.asJava.classes
|
package org.jetbrains.kotlin.asJava.classes
|
||||||
|
|
||||||
import com.intellij.testFramework.LightProjectDescriptor
|
import com.intellij.testFramework.LightProjectDescriptor
|
||||||
|
import org.jetbrains.kotlin.asJava.LightClassGenerationSupport
|
||||||
import org.jetbrains.kotlin.idea.perf.UltraLightChecker
|
import org.jetbrains.kotlin.idea.perf.UltraLightChecker
|
||||||
import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightFixtureTestCase
|
import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightFixtureTestCase
|
||||||
import org.jetbrains.kotlin.idea.test.KotlinWithJdkAndRuntimeLightProjectDescriptor
|
import org.jetbrains.kotlin.idea.test.KotlinWithJdkAndRuntimeLightProjectDescriptor
|
||||||
|
import org.jetbrains.kotlin.psi.KtClassOrObject
|
||||||
import org.jetbrains.kotlin.psi.KtFile
|
import org.jetbrains.kotlin.psi.KtFile
|
||||||
|
import org.jetbrains.kotlin.test.KotlinTestUtils
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
abstract class AbstractUltraLightClassLoadingTest : KotlinLightCodeInsightFixtureTestCase() {
|
abstract class AbstractUltraLightClassLoadingTest : KotlinLightCodeInsightFixtureTestCase() {
|
||||||
@@ -17,17 +20,42 @@ abstract class AbstractUltraLightClassLoadingTest : KotlinLightCodeInsightFixtur
|
|||||||
|
|
||||||
fun doTest(testDataPath: String) {
|
fun doTest(testDataPath: String) {
|
||||||
val file = myFixture.addFileToProject(testDataPath, File(testDataPath).readText()) as KtFile
|
val file = myFixture.addFileToProject(testDataPath, File(testDataPath).readText()) as KtFile
|
||||||
|
|
||||||
|
val expectedTextFile = File(testDataPath.replaceFirst("\\.kt\$".toRegex(), ".java"))
|
||||||
|
if (expectedTextFile.exists()) {
|
||||||
|
val renderedResult =
|
||||||
|
UltraLightChecker.allClasses(file).mapNotNull { ktClass ->
|
||||||
|
LightClassGenerationSupport.getInstance(ktClass.project).createUltraLightClass(ktClass)?.let { it to ktClass }
|
||||||
|
}.joinToString("\n\n") { (ultraLightClass, ktClass) ->
|
||||||
|
with(UltraLightChecker) {
|
||||||
|
ultraLightClass.renderClass().also {
|
||||||
|
checkClassLoadingExpectations(ktClass, ultraLightClass)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
KotlinTestUtils.assertEqualsToFile(expectedTextFile, renderedResult)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
for (ktClass in UltraLightChecker.allClasses(file)) {
|
for (ktClass in UltraLightChecker.allClasses(file)) {
|
||||||
val clsLoadingExpected = ktClass.docComment?.text?.contains("should load cls") == true
|
|
||||||
val ultraLightClass = UltraLightChecker.checkClassEquivalence(ktClass)
|
val ultraLightClass = UltraLightChecker.checkClassEquivalence(ktClass)
|
||||||
if (ultraLightClass != null) {
|
if (ultraLightClass != null) {
|
||||||
assertEquals(
|
checkClassLoadingExpectations(ktClass, ultraLightClass)
|
||||||
"Cls-loaded status differs from expected for ${ultraLightClass.qualifiedName}",
|
|
||||||
clsLoadingExpected,
|
|
||||||
ultraLightClass.isClsDelegateLoaded
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
private fun checkClassLoadingExpectations(
|
||||||
|
ktClass: KtClassOrObject,
|
||||||
|
ultraLightClass: KtUltraLightClass
|
||||||
|
) {
|
||||||
|
val clsLoadingExpected = ktClass.docComment?.text?.contains("should load cls") == true
|
||||||
|
assertEquals(
|
||||||
|
"Cls-loaded status differs from expected for ${ultraLightClass.qualifiedName}",
|
||||||
|
clsLoadingExpected,
|
||||||
|
ultraLightClass.isClsDelegateLoaded
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user