[FIR] Resolve arguments of error types during type resolution
This commit is contained in:
committed by
Space Team
parent
bc93556dbb
commit
bd26c29229
+1
-1
@@ -1,2 +1,2 @@
|
||||
KtType: suspend (kotlin.Int) -> kotlin.Unit
|
||||
PsiType: PsiType:Function2<? super Integer, ? super NonExistentClass, ? extends Object>
|
||||
PsiType: PsiType:Function2<? super Integer, ? super NonExistentClass<Unit>, ? extends Object>
|
||||
|
||||
+1
-1
@@ -1,2 +1,2 @@
|
||||
Resolved to:
|
||||
0: (in Controller) suspend fun suspendHere(x: ERROR_TYPE(Symbol not found for Continuation))
|
||||
0: (in Controller) suspend fun suspendHere(x: ERROR_TYPE(Symbol not found for Continuation))
|
||||
|
||||
+2
-1
@@ -12,11 +12,12 @@ import org.jetbrains.kotlin.analysis.api.KtAllowAnalysisOnEdt
|
||||
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
|
||||
import org.jetbrains.kotlin.analysis.api.analyze
|
||||
import org.jetbrains.kotlin.analysis.api.lifetime.allowAnalysisOnEdt
|
||||
import org.jetbrains.kotlin.builtins.StandardNames
|
||||
import org.jetbrains.kotlin.analysis.project.structure.KtModule
|
||||
import org.jetbrains.kotlin.psi.KtElement
|
||||
|
||||
internal fun PsiElement.nonExistentType(): PsiType =
|
||||
JavaPsiFacade.getElementFactory(project).createTypeFromText("error.NonExistentClass", this)
|
||||
JavaPsiFacade.getElementFactory(project).createTypeFromText(StandardNames.NON_EXISTENT_CLASS.asString(), this)
|
||||
|
||||
@OptIn(KtAllowAnalysisOnEdt::class)
|
||||
private inline fun <E> allowLightClassesOnEdt(crossinline action: () -> E): E = allowAnalysisOnEdt(action)
|
||||
|
||||
@@ -59,7 +59,6 @@ import org.jetbrains.kotlin.resolve.calls.model.VarargValueArgument
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.classId
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.isPublishedApi
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.module
|
||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes.DEFAULT_CONSTRUCTOR_MARKER
|
||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes.OBJECT_TYPE
|
||||
import org.jetbrains.kotlin.resolve.jvm.JAVA_LANG_RECORD_FQ_NAME
|
||||
@@ -76,6 +75,7 @@ import org.jetbrains.kotlin.types.checker.convertVariance
|
||||
import org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils.*
|
||||
import org.jetbrains.kotlin.types.model.*
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.zipWithNulls
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes.*
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
import org.jetbrains.org.objectweb.asm.commons.Method
|
||||
@@ -1446,10 +1446,10 @@ class KotlinTypeMapper @JvmOverloads constructor(
|
||||
SimpleClassicTypeSystemContext.getVarianceForWildcard(parameter, projection, mode)
|
||||
|
||||
private fun TypeSystemCommonBackendContext.getVarianceForWildcard(
|
||||
parameter: TypeParameterMarker, projection: TypeArgumentMarker, mode: TypeMappingMode
|
||||
parameter: TypeParameterMarker?, projection: TypeArgumentMarker, mode: TypeMappingMode
|
||||
): Variance {
|
||||
val projectionKind = projection.getVariance().convertVariance()
|
||||
val parameterVariance = parameter.getVariance().convertVariance()
|
||||
val parameterVariance = parameter?.getVariance()?.convertVariance() ?: Variance.INVARIANT
|
||||
|
||||
if (parameterVariance == Variance.INVARIANT) {
|
||||
return projectionKind
|
||||
@@ -1465,7 +1465,7 @@ class KotlinTypeMapper @JvmOverloads constructor(
|
||||
return Variance.INVARIANT
|
||||
}
|
||||
|
||||
if (parameterVariance == Variance.IN_VARIANCE && isMostPreciseContravariantArgument(projection.getType(), parameter)) {
|
||||
if (parameterVariance == Variance.IN_VARIANCE && isMostPreciseContravariantArgument(projection.getType())) {
|
||||
return Variance.INVARIANT
|
||||
}
|
||||
}
|
||||
@@ -1484,10 +1484,11 @@ class KotlinTypeMapper @JvmOverloads constructor(
|
||||
mode: TypeMappingMode,
|
||||
mapType: (KotlinTypeMarker, JvmSignatureWriter, TypeMappingMode) -> Type
|
||||
) {
|
||||
for ((parameter, argument) in parameters.zip(arguments)) {
|
||||
for ((parameter, argument) in parameters.zipWithNulls(arguments)) {
|
||||
if (argument == null) break
|
||||
if (argument.isStarProjection() ||
|
||||
// In<Nothing, Foo> == In<*, Foo> -> In<?, Foo>
|
||||
argument.getType().isNothing() && parameter.getVariance() == TypeVariance.IN
|
||||
argument.getType().isNothing() && parameter?.getVariance() == TypeVariance.IN
|
||||
) {
|
||||
signatureVisitor.writeUnboundedWildcard()
|
||||
} else {
|
||||
@@ -1496,10 +1497,11 @@ class KotlinTypeMapper @JvmOverloads constructor(
|
||||
|
||||
signatureVisitor.writeTypeArgument(projectionKind)
|
||||
|
||||
val parameterVariance = parameter?.getVariance()?.convertVariance() ?: Variance.INVARIANT
|
||||
mapType(
|
||||
argument.getType(), signatureVisitor,
|
||||
argumentMode.toGenericArgumentMode(
|
||||
getEffectiveVariance(parameter.getVariance().convertVariance(), argument.getVariance().convertVariance())
|
||||
getEffectiveVariance(parameterVariance, argument.getVariance().convertVariance())
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@@ -37,11 +37,8 @@ import org.jetbrains.kotlin.types.checker.SimpleClassicTypeSystemContext
|
||||
import org.jetbrains.kotlin.types.checker.convertVariance
|
||||
import org.jetbrains.kotlin.types.getEffectiveVariance
|
||||
import org.jetbrains.kotlin.types.model.KotlinTypeMarker
|
||||
import org.jetbrains.kotlin.types.model.TypeParameterMarker
|
||||
|
||||
// TODO: probably class upper bound should be used
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
fun TypeSystemCommonBackendContext.isMostPreciseContravariantArgument(type: KotlinTypeMarker, parameter: TypeParameterMarker): Boolean =
|
||||
fun TypeSystemCommonBackendContext.isMostPreciseContravariantArgument(type: KotlinTypeMarker): Boolean =
|
||||
type.typeConstructor().isAnyConstructor()
|
||||
|
||||
fun TypeSystemCommonBackendContext.isMostPreciseCovariantArgument(type: KotlinTypeMarker): Boolean =
|
||||
@@ -62,7 +59,7 @@ private fun TypeSystemCommonBackendContext.canHaveSubtypesIgnoringNullability(ko
|
||||
|
||||
val effectiveVariance = getEffectiveVariance(parameter.getVariance().convertVariance(), projectionKind)
|
||||
if (effectiveVariance == Variance.OUT_VARIANCE && !isMostPreciseCovariantArgument(type)) return true
|
||||
if (effectiveVariance == Variance.IN_VARIANCE && !isMostPreciseContravariantArgument(type, parameter)) return true
|
||||
if (effectiveVariance == Variance.IN_VARIANCE && !isMostPreciseContravariantArgument(type)) return true
|
||||
}
|
||||
|
||||
return false
|
||||
|
||||
@@ -43,14 +43,12 @@ class ConeClassLikeErrorLookupTag(override val classId: ClassId) : ConeClassLike
|
||||
class ConeErrorType(
|
||||
val diagnostic: ConeDiagnostic,
|
||||
val isUninferredParameter: Boolean = false,
|
||||
override val typeArguments: Array<out ConeTypeProjection> = EMPTY_ARRAY,
|
||||
override val attributes: ConeAttributes = ConeAttributes.Empty
|
||||
) : ConeClassLikeType() {
|
||||
override val lookupTag: ConeClassLikeLookupTag
|
||||
get() = ConeClassLikeErrorLookupTag(ClassId.fromString("<error>"))
|
||||
|
||||
override val typeArguments: Array<out ConeTypeProjection>
|
||||
get() = EMPTY_ARRAY
|
||||
|
||||
override val nullability: ConeNullability
|
||||
get() = ConeNullability.UNKNOWN
|
||||
|
||||
|
||||
+30
-13
@@ -5,6 +5,7 @@
|
||||
|
||||
package org.jetbrains.kotlin.fir.backend.jvm
|
||||
|
||||
import org.jetbrains.kotlin.builtins.StandardNames
|
||||
import org.jetbrains.kotlin.builtins.functions.BuiltInFunctionArity
|
||||
import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMap
|
||||
import org.jetbrains.kotlin.codegen.signature.JvmSignatureWriter
|
||||
@@ -25,6 +26,7 @@ import org.jetbrains.kotlin.fir.resolve.toSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.ConeClassLikeLookupTag
|
||||
import org.jetbrains.kotlin.fir.symbols.ConeClassifierLookupTag
|
||||
import org.jetbrains.kotlin.fir.symbols.ConeTypeParameterLookupTag
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.ConeClassLikeLookupTagImpl
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirTypeAliasSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirTypeParameterSymbol
|
||||
@@ -43,9 +45,16 @@ import org.jetbrains.kotlin.types.model.SimpleTypeMarker
|
||||
import org.jetbrains.kotlin.types.model.TypeConstructorMarker
|
||||
import org.jetbrains.kotlin.types.model.TypeParameterMarker
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.runIf
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.runUnless
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
|
||||
class FirJvmTypeMapper(val session: FirSession) : TypeMappingContext<JvmSignatureWriter>, FirSessionComponent {
|
||||
companion object {
|
||||
val NON_EXISTENT_ID = ClassId.topLevel(StandardNames.NON_EXISTENT_CLASS)
|
||||
private val typeForNonExistentClass = ConeClassLikeLookupTagImpl(NON_EXISTENT_ID)
|
||||
.constructClassType(emptyArray(), isNullable = false)
|
||||
}
|
||||
|
||||
override val typeContext = ConeTypeSystemCommonBackendContextForTypeMapping(session.typeContext)
|
||||
|
||||
fun mapType(type: ConeKotlinType, mode: TypeMappingMode = TypeMappingMode.DEFAULT, sw: JvmSignatureWriter? = null): Type {
|
||||
@@ -67,7 +76,7 @@ class FirJvmTypeMapper(val session: FirSession) : TypeMappingContext<JvmSignatur
|
||||
return
|
||||
}
|
||||
|
||||
val possiblyInnerType = type.buildPossiblyInnerType() ?: error("possiblyInnerType with arguments should not be null")
|
||||
val possiblyInnerType = type.buildPossiblyInnerType()
|
||||
|
||||
val innerTypesAsList = possiblyInnerType.segments()
|
||||
|
||||
@@ -78,7 +87,7 @@ class FirJvmTypeMapper(val session: FirSession) : TypeMappingContext<JvmSignatur
|
||||
} else {
|
||||
val outerType = innerTypesAsList[indexOfParameterizedType]
|
||||
|
||||
writeOuterClassBegin(asmType, mapType(outerType.classifier.fir.defaultType()).internalName)
|
||||
writeOuterClassBegin(asmType, mapType(outerType.classifier?.fir?.defaultType() ?: typeForNonExistentClass).internalName)
|
||||
writeGenericArguments(this, outerType, mode)
|
||||
|
||||
writeInnerParts(
|
||||
@@ -96,8 +105,12 @@ class FirJvmTypeMapper(val session: FirSession) : TypeMappingContext<JvmSignatur
|
||||
typeContext.hasNothingInNonContravariantPosition(type)
|
||||
}
|
||||
|
||||
private fun ConeKotlinType.buildPossiblyInnerType(): PossiblyInnerConeType? {
|
||||
if (this !is ConeClassLikeType) return null
|
||||
private fun ConeKotlinType.buildPossiblyInnerType(): PossiblyInnerConeType {
|
||||
fun createForError(): PossiblyInnerConeType {
|
||||
return PossiblyInnerConeType(classifier = null, typeArguments.toList(), outerType = null)
|
||||
}
|
||||
|
||||
if (this !is ConeClassLikeType) return createForError()
|
||||
|
||||
return when (val symbol = lookupTag.toSymbol(session)) {
|
||||
is FirRegularClassSymbol -> buildPossiblyInnerType(symbol, 0)
|
||||
@@ -107,7 +120,7 @@ class FirJvmTypeMapper(val session: FirSession) : TypeMappingContext<JvmSignatur
|
||||
classSymbol?.let { expandedType.buildPossiblyInnerType(it, 0) }
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
} ?: createForError()
|
||||
}
|
||||
|
||||
private fun ConeClassLikeType.parentClassOrNull(): FirRegularClassSymbol? {
|
||||
@@ -135,7 +148,7 @@ class FirJvmTypeMapper(val session: FirSession) : TypeMappingContext<JvmSignatur
|
||||
}
|
||||
|
||||
private class PossiblyInnerConeType(
|
||||
val classifier: FirRegularClassSymbol,
|
||||
val classifier: FirRegularClassSymbol?,
|
||||
val arguments: List<ConeTypeProjection>,
|
||||
private val outerType: PossiblyInnerConeType?
|
||||
) {
|
||||
@@ -147,9 +160,9 @@ class FirJvmTypeMapper(val session: FirSession) : TypeMappingContext<JvmSignatur
|
||||
type: PossiblyInnerConeType,
|
||||
mode: TypeMappingMode
|
||||
) {
|
||||
val classifier = type.classifier.fir
|
||||
val defaultType = classifier.defaultType()
|
||||
val parameters = classifier.typeParameters.map { it.symbol }
|
||||
val classifier = type.classifier?.fir
|
||||
val defaultType = classifier?.defaultType() ?: typeForNonExistentClass
|
||||
val parameters = classifier?.typeParameters.orEmpty().map { it.symbol }
|
||||
val arguments = type.arguments
|
||||
|
||||
if ((defaultType.isFunctionalType(session) && arguments.size > BuiltInFunctionArity.BIG_ARITY)
|
||||
@@ -183,16 +196,20 @@ class FirJvmTypeMapper(val session: FirSession) : TypeMappingContext<JvmSignatur
|
||||
index: Int
|
||||
) {
|
||||
for (innerPart in innerTypesAsList.subList(index, innerTypesAsList.size)) {
|
||||
sw.writeInnerClass(getJvmShortName(innerPart.classifier.fir))
|
||||
sw.writeInnerClass(getJvmShortName(innerPart.classifier?.classId ?: NON_EXISTENT_ID))
|
||||
writeGenericArguments(sw, innerPart, mode)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun getJvmShortName(klass: FirRegularClass): String {
|
||||
val result = runIf(!klass.isLocal) {
|
||||
klass.classId.asSingleFqName().toUnsafe().let { JavaToKotlinClassMap.mapKotlinToJava(it)?.shortClassName?.asString() }
|
||||
return getJvmShortName(klass.classId)
|
||||
}
|
||||
|
||||
internal fun getJvmShortName(classId: ClassId): String {
|
||||
val result = runUnless(classId.isLocal) {
|
||||
classId.asSingleFqName().toUnsafe().let { JavaToKotlinClassMap.mapKotlinToJava(it)?.shortClassName?.asString() }
|
||||
}
|
||||
return result ?: SpecialNames.safeIdentifier(klass.name).identifier
|
||||
return result ?: SpecialNames.safeIdentifier(classId.shortClassName).identifier
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -118,7 +118,7 @@ fun <T : ConeKotlinType> T.withArguments(arguments: Array<out ConeTypeProjection
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return when (this) {
|
||||
is ConeErrorType -> this
|
||||
is ConeErrorType -> ConeErrorType(diagnostic, isUninferredParameter, arguments, attributes) as T
|
||||
is ConeClassLikeTypeImpl -> ConeClassLikeTypeImpl(lookupTag, arguments, nullability.isNullable) as T
|
||||
is ConeDefinitelyNotNullType -> ConeDefinitelyNotNullType(original.withArguments(arguments)) as T
|
||||
else -> error("Not supported: $this: ${this.renderForDebugging()}")
|
||||
|
||||
+1
-1
@@ -127,7 +127,7 @@ private class CalculatorForNestedCall(
|
||||
) {
|
||||
val unwrappedType = type.lowerBoundIfFlexible()
|
||||
val typeArgumentsCount = unwrappedType.argumentsCount()
|
||||
if (typeArgumentsCount > 0) {
|
||||
if (typeArgumentsCount > 0 && !unwrappedType.isError()) {
|
||||
for (position in 0 until typeArgumentsCount) {
|
||||
val argument = unwrappedType.getArgument(position)
|
||||
val parameter = unwrappedType.typeConstructor().getParameter(position)
|
||||
|
||||
+37
-29
@@ -235,34 +235,6 @@ class FirTypeResolverImpl(private val session: FirSession) : FirTypeResolver() {
|
||||
TypeResolutionResult.Unresolved -> null to null
|
||||
}
|
||||
|
||||
if (symbol == null || symbol !is FirClassifierSymbol<*>) {
|
||||
val diagnostic = when {
|
||||
symbol?.fir is FirEnumEntry -> {
|
||||
if (isOperandOfIsOperator) {
|
||||
ConeSimpleDiagnostic("'is' operator can not be applied to an enum entry.", DiagnosticKind.IsEnumEntry)
|
||||
} else {
|
||||
ConeSimpleDiagnostic("An enum entry should not be used as a type.", DiagnosticKind.EnumEntryAsType)
|
||||
}
|
||||
}
|
||||
result is TypeResolutionResult.Ambiguity -> {
|
||||
ConeAmbiguityError(typeRef.qualifier.last().name, result.typeCandidates.first().applicability, result.typeCandidates)
|
||||
}
|
||||
else -> {
|
||||
ConeUnresolvedTypeQualifierError(typeRef.qualifier, isNullable = typeRef.isMarkedNullable)
|
||||
}
|
||||
}
|
||||
return ConeErrorType(diagnostic, attributes = typeRef.annotations.computeTypeAttributes(session))
|
||||
}
|
||||
if (symbol is FirTypeParameterSymbol) {
|
||||
for (part in typeRef.qualifier) {
|
||||
if (part.typeArgumentList.typeArguments.isNotEmpty()) {
|
||||
return ConeErrorType(
|
||||
ConeUnexpectedTypeArgumentsError("Type arguments not allowed", part.typeArgumentList.source)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val allTypeArguments = mutableListOf<ConeTypeProjection>()
|
||||
var typeArgumentsCount = 0
|
||||
|
||||
@@ -351,8 +323,44 @@ class FirTypeResolverImpl(private val session: FirSession) : FirTypeResolver() {
|
||||
}
|
||||
}
|
||||
|
||||
val resultingArguments = allTypeArguments.toTypedArray()
|
||||
|
||||
if (symbol == null || symbol !is FirClassifierSymbol<*>) {
|
||||
val diagnostic = when {
|
||||
symbol?.fir is FirEnumEntry -> {
|
||||
if (isOperandOfIsOperator) {
|
||||
ConeSimpleDiagnostic("'is' operator can not be applied to an enum entry.", DiagnosticKind.IsEnumEntry)
|
||||
} else {
|
||||
ConeSimpleDiagnostic("An enum entry should not be used as a type.", DiagnosticKind.EnumEntryAsType)
|
||||
}
|
||||
}
|
||||
result is TypeResolutionResult.Ambiguity -> {
|
||||
ConeAmbiguityError(typeRef.qualifier.last().name, result.typeCandidates.first().applicability, result.typeCandidates)
|
||||
}
|
||||
else -> {
|
||||
ConeUnresolvedTypeQualifierError(typeRef.qualifier, isNullable = typeRef.isMarkedNullable)
|
||||
}
|
||||
}
|
||||
return ConeErrorType(
|
||||
diagnostic,
|
||||
typeArguments = resultingArguments,
|
||||
attributes = typeRef.annotations.computeTypeAttributes(session)
|
||||
)
|
||||
}
|
||||
|
||||
if (symbol is FirTypeParameterSymbol) {
|
||||
for (part in typeRef.qualifier) {
|
||||
if (part.typeArgumentList.typeArguments.isNotEmpty()) {
|
||||
return ConeErrorType(
|
||||
ConeUnexpectedTypeArgumentsError("Type arguments not allowed", part.typeArgumentList.source),
|
||||
typeArguments = resultingArguments
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return symbol.constructType(
|
||||
allTypeArguments.toTypedArray(),
|
||||
resultingArguments,
|
||||
typeRef.isMarkedNullable,
|
||||
typeRef.annotations.computeTypeAttributes(session)
|
||||
).also {
|
||||
|
||||
@@ -17,6 +17,7 @@ import org.jetbrains.annotations.NotNull
|
||||
import org.jetbrains.kotlin.asJava.LightClassGenerationSupport
|
||||
import org.jetbrains.kotlin.asJava.builder.LightMemberOriginForDeclaration
|
||||
import org.jetbrains.kotlin.asJava.elements.*
|
||||
import org.jetbrains.kotlin.builtins.StandardNames
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
|
||||
@@ -142,7 +143,7 @@ internal open class KtUltraLightFieldImpl protected constructor(
|
||||
get() = type
|
||||
|
||||
private val _type: PsiType by lazyPub {
|
||||
fun nonExistent() = JavaPsiFacade.getElementFactory(project).createTypeFromText("error.NonExistentClass", declaration)
|
||||
fun nonExistent() = JavaPsiFacade.getElementFactory(project).createTypeFromText(StandardNames.NON_EXISTENT_CLASS.asString(), declaration)
|
||||
|
||||
when {
|
||||
(declaration is KtProperty && declaration.hasDelegate()) || declaration is KtEnumEntry || declaration is KtObjectDeclaration ->
|
||||
|
||||
@@ -79,6 +79,8 @@ object StandardNames {
|
||||
@JvmField
|
||||
val KOTLIN_INTERNAL_FQ_NAME = BUILT_INS_PACKAGE_FQ_NAME.child(Name.identifier("internal"))
|
||||
|
||||
val NON_EXISTENT_CLASS = FqName("error.NonExistentClass")
|
||||
|
||||
@JvmField
|
||||
val BUILT_INS_PACKAGE_FQ_NAMES = setOf(
|
||||
BUILT_INS_PACKAGE_FQ_NAME,
|
||||
|
||||
@@ -300,3 +300,23 @@ fun <T : Enum<T>> enumSetOf(element: T, vararg elements: T): EnumSet<T> = EnumSe
|
||||
fun shouldNotBeCalled(message: String = "should not be called"): Nothing {
|
||||
error(message)
|
||||
}
|
||||
|
||||
private inline fun <T, R> Iterable<T>.zipWithDefault(other: Iterable<R>, leftDefault: () -> T, rightDefault: () -> R): List<Pair<T, R>> {
|
||||
val leftIterator = this.iterator()
|
||||
val rightIterator = other.iterator()
|
||||
return buildList {
|
||||
while (leftIterator.hasNext() && rightIterator.hasNext()) {
|
||||
add(leftIterator.next() to rightIterator.next())
|
||||
}
|
||||
while (leftIterator.hasNext()) {
|
||||
add(leftIterator.next() to rightDefault())
|
||||
}
|
||||
while (rightIterator.hasNext()) {
|
||||
add(leftDefault() to rightIterator.next())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun <T, R> Iterable<T>.zipWithNulls(other: Iterable<R>): List<Pair<T?, R?>> {
|
||||
return zipWithDefault(other, { null }, { null })
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user