[JS] Don't fail on recursive upper bounds while resolving JsExports

Fixes https://youtrack.jetbrains.com/issue/KT-42112
This commit is contained in:
Shagen Ogandzhanian
2020-11-23 17:59:03 +01:00
parent f9a032dbfa
commit 3282e092d8
6 changed files with 141 additions and 33 deletions
@@ -19,7 +19,6 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.isEffectivelyExternal
import org.jetbrains.kotlin.resolve.descriptorUtil.isExtensionProperty
import org.jetbrains.kotlin.resolve.inline.isInlineWithReified
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.TypeProjection
import org.jetbrains.kotlin.types.isDynamic
import org.jetbrains.kotlin.types.typeUtil.*
@@ -30,7 +29,7 @@ object JsExportDeclarationChecker : DeclarationChecker {
fun checkTypeParameter(descriptor: TypeParameterDescriptor) {
for (upperBound in descriptor.upperBounds) {
if (!isTypeExportable(upperBound, bindingContext)) {
if (!upperBound.isExportable(bindingContext)) {
val typeParameterDeclaration = DescriptorToSourceUtils.descriptorToDeclaration(descriptor)!!
trace.report(ErrorsJs.NON_EXPORTABLE_TYPE.on(typeParameterDeclaration, "upper bound", upperBound))
}
@@ -38,7 +37,7 @@ object JsExportDeclarationChecker : DeclarationChecker {
}
fun checkValueParameter(descriptor: ValueParameterDescriptor) {
if (!isTypeExportable(descriptor.type, bindingContext)) {
if (!descriptor.type.isExportable(bindingContext)) {
val valueParameterDeclaration = DescriptorToSourceUtils.descriptorToDeclaration(descriptor)!!
trace.report(ErrorsJs.NON_EXPORTABLE_TYPE.on(valueParameterDeclaration, "parameter", descriptor.type))
}
@@ -86,7 +85,7 @@ object JsExportDeclarationChecker : DeclarationChecker {
}
descriptor.returnType?.let { returnType ->
if (!isTypeExportable(returnType, bindingContext, true)) {
if (!returnType.isExportableReturn(bindingContext)) {
trace.report(ErrorsJs.NON_EXPORTABLE_TYPE.on(declaration, "return", returnType))
}
}
@@ -98,7 +97,7 @@ object JsExportDeclarationChecker : DeclarationChecker {
reportWrongExportedDeclaration("extension property")
return
}
if (!isTypeExportable(descriptor.type, bindingContext)) {
if (!descriptor.type.isExportable(bindingContext)) {
trace.report(ErrorsJs.NON_EXPORTABLE_TYPE.on(declaration, "property", descriptor.type))
}
}
@@ -122,7 +121,7 @@ object JsExportDeclarationChecker : DeclarationChecker {
}
for (superType in descriptor.defaultType.supertypes()) {
if (!isTypeExportable(superType, bindingContext)) {
if (!superType.isExportable(bindingContext)) {
trace.report(ErrorsJs.NON_EXPORTABLE_TYPE.on(declaration, "super", superType))
}
}
@@ -130,45 +129,58 @@ object JsExportDeclarationChecker : DeclarationChecker {
}
}
private fun KotlinType.isExportableReturn(bindingContext: BindingContext, currentlyProcessed: MutableSet<KotlinType> = mutableSetOf()) =
isUnit() || isExportable(bindingContext, currentlyProcessed)
private fun isTypeExportable(type: KotlinType, bindingContext: BindingContext, isReturnType: Boolean = false): Boolean {
if (isReturnType && type.isUnit())
private fun KotlinType.isExportable(
bindingContext: BindingContext,
currentlyProcessed: MutableSet<KotlinType> = mutableSetOf()
): Boolean {
if (!currentlyProcessed.add(this)) {
return true
}
if (type.isFunctionType) {
val arguments = type.arguments
val argumentsSize = type.arguments.size - 1
for (i in 0 until argumentsSize) {
if (!isTypeExportable(arguments[i].type, bindingContext))
currentlyProcessed.add(this)
if (isFunctionType) {
for (i in 0 until arguments.lastIndex) {
if (!arguments[i].type.isExportable(bindingContext, currentlyProcessed)) {
currentlyProcessed.remove(this)
return false
}
}
if (!isTypeExportable(arguments.last().type, bindingContext, isReturnType = true))
return false
return true
currentlyProcessed.remove(this)
return arguments.last().type.isExportableReturn(bindingContext, currentlyProcessed)
}
for (argument: TypeProjection in type.arguments) {
if (!isTypeExportable(argument.type, bindingContext))
for (argument in arguments) {
if (!argument.type.isExportable(bindingContext, currentlyProcessed)) {
currentlyProcessed.remove(this)
return false
}
}
val nonNullable = type.makeNotNullable()
currentlyProcessed.remove(this)
// Is primitive exportable type
if (nonNullable.isAnyOrNullableAny() ||
nonNullable.isDynamic() ||
nonNullable.isBoolean() ||
KotlinBuiltIns.isThrowableOrNullableThrowable(nonNullable) ||
KotlinBuiltIns.isString(nonNullable) ||
(nonNullable.isPrimitiveNumberOrNullableType() && !nonNullable.isLong()) ||
nonNullable.isNothingOrNullableNothing() ||
(KotlinBuiltIns.isArray(type)) ||
KotlinBuiltIns.isPrimitiveArray(type)
) return true
val nonNullable = makeNotNullable()
val descriptor = type.constructor.declarationDescriptor ?: return false
return descriptor is MemberDescriptor && descriptor.isEffectivelyExternal() ||
AnnotationsUtils.isExportedObject(descriptor, bindingContext)
val isPrimitiveExportableType = nonNullable.isAnyOrNullableAny() ||
nonNullable.isDynamic() ||
nonNullable.isBoolean() ||
KotlinBuiltIns.isThrowableOrNullableThrowable(nonNullable) ||
KotlinBuiltIns.isString(nonNullable) ||
(nonNullable.isPrimitiveNumberOrNullableType() && !nonNullable.isLong()) ||
nonNullable.isNothingOrNullableNothing() ||
KotlinBuiltIns.isArray(this) ||
KotlinBuiltIns.isPrimitiveArray(this)
if (isPrimitiveExportableType) return true
val descriptor = constructor.declarationDescriptor
if (descriptor !is MemberDescriptor) return false
return descriptor.isEffectivelyExternal() || AnnotationsUtils.isExportedObject(descriptor, bindingContext)
}
}