Unify WhenMissingCase from FIR and FE1.0

This commit is contained in:
Ilya Kirillov
2021-02-24 12:23:19 +01:00
parent f4371c670e
commit f19a9af7b3
7 changed files with 47 additions and 50 deletions
@@ -22,7 +22,9 @@ import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.diagnostics.WhenMissingCase
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.name.CallableId
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.checkReservedPrefixWord
import org.jetbrains.kotlin.resolve.BindingContext
@@ -40,19 +42,9 @@ import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.TypeUtils
import java.util.*
sealed class WhenMissingCase {
abstract val branchConditionText: String
}
// Always must be first in the list
object UnknownMissingCase : WhenMissingCase() {
override fun toString() = "unknown"
override val branchConditionText = "else"
}
val List<WhenMissingCase>.hasUnknown: Boolean
get() = firstOrNull() == UnknownMissingCase
get() = firstOrNull() == WhenMissingCase.Unknown
private interface WhenExhaustivenessChecker {
fun getMissingCases(
@@ -65,12 +57,6 @@ private interface WhenExhaustivenessChecker {
fun isApplicable(subjectType: KotlinType): Boolean = false
}
object NullMissingCase : WhenMissingCase() {
override fun toString() = branchConditionText
override val branchConditionText = "null"
}
// It's not a regular exhaustiveness checker, invoke it only inside other checkers
private object WhenOnNullableExhaustivenessChecker /* : WhenExhaustivenessChecker*/ {
fun getMissingCases(expression: KtWhenExpression, context: BindingContext, nullable: Boolean) =
@@ -89,15 +75,10 @@ private object WhenOnNullableExhaustivenessChecker /* : WhenExhaustivenessChecke
}
}
}
return listOf(NullMissingCase)
return listOf(WhenMissingCase.NullIsMissing)
}
}
class BooleanMissingCase(val b: Boolean) : WhenMissingCase() {
override fun toString() = branchConditionText
override val branchConditionText = b.toString()
}
private object WhenOnBooleanExhaustivenessChecker : WhenExhaustivenessChecker {
override fun getMissingCases(
@@ -118,8 +99,8 @@ private object WhenOnBooleanExhaustivenessChecker : WhenExhaustivenessChecker {
}
}
}
return (if (!containsTrue) listOf(BooleanMissingCase(true)) else listOf()) +
(if (!containsFalse) listOf(BooleanMissingCase(false)) else listOf()) +
return (if (!containsTrue) listOf(WhenMissingCase.BooleanIsMissing.True) else listOf()) +
(if (!containsFalse) listOf(WhenMissingCase.BooleanIsMissing.False) else listOf()) +
WhenOnNullableExhaustivenessChecker.getMissingCases(expression, context, nullable)
}
@@ -128,15 +109,6 @@ private object WhenOnBooleanExhaustivenessChecker : WhenExhaustivenessChecker {
}
}
class ClassMissingCase(val descriptor: ClassDescriptor) : WhenMissingCase() {
val classIsSingleton get() = descriptor.kind.isSingleton
val classFqName get() = DescriptorUtils.getFqNameFromTopLevelClass(descriptor)
override fun toString() = descriptor.name.identifier.let { if (classIsSingleton) it else "is $it" }
override val branchConditionText = classFqName.asString().let { if (classIsSingleton) it else "is $it" }
}
internal abstract class WhenOnClassExhaustivenessChecker : WhenExhaustivenessChecker {
private fun getReference(expression: KtExpression?): KtSimpleNameExpression? =
@@ -195,7 +167,7 @@ internal abstract class WhenOnClassExhaustivenessChecker : WhenExhaustivenessChe
context: BindingContext
): List<WhenMissingCase> {
// when on empty enum / sealed is considered non-exhaustive, see test whenOnEmptySealed
if (subclasses.isEmpty()) return listOf(UnknownMissingCase)
if (subclasses.isEmpty()) return listOf(WhenMissingCase.Unknown)
val checkedDescriptors = linkedSetOf<ClassDescriptor>()
for (whenEntry in whenExpression.entries) {
@@ -219,7 +191,20 @@ internal abstract class WhenOnClassExhaustivenessChecker : WhenExhaustivenessChe
}
}
}
return (subclasses - checkedDescriptors).map(::ClassMissingCase)
return (subclasses - checkedDescriptors).map(::createWhenMissingCaseForClassOrEnum)
}
private fun createWhenMissingCaseForClassOrEnum(classDescriptor: ClassDescriptor): WhenMissingCase {
val classId = DescriptorUtils.getClassIdForNonLocalClass(classDescriptor)
return if (classDescriptor.kind != ClassKind.ENUM_ENTRY) {
WhenMissingCase.IsTypeCheckIsMissing(
classId = DescriptorUtils.getClassIdForNonLocalClass(classDescriptor),
isSingleton = classDescriptor.kind.isSingleton
)
} else {
val enumClassId = classId.outerClassId ?: error("Enum should have class id")
WhenMissingCase.EnumCheckIsMissing(CallableId(enumClassId, classId.shortClassName))
}
}
}
@@ -329,10 +314,10 @@ object WhenChecker {
) = WhenOnSealedExhaustivenessChecker.getMissingCases(expression, context, sealedClassDescriptor, false)
fun getMissingCases(expression: KtWhenExpression, context: BindingContext): List<WhenMissingCase> {
val type = whenSubjectType(expression, context) ?: return listOf(UnknownMissingCase)
val type = whenSubjectType(expression, context) ?: return listOf(WhenMissingCase.Unknown)
val nullable = type.isMarkedNullable
val checkers = exhaustivenessCheckers.filter { it.isApplicable(type) }
if (checkers.isEmpty()) return listOf(UnknownMissingCase)
if (checkers.isEmpty()) return listOf(WhenMissingCase.Unknown)
return checkers.map { it.getMissingCases(expression, context, TypeUtils.getClassDescriptor(type), nullable) }.flatten()
}
@@ -10,7 +10,6 @@ import com.intellij.psi.PsiElement;
import com.intellij.psi.impl.source.tree.LeafPsiElement;
import kotlin.Pair;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.kotlin.cfg.WhenMissingCase;
import org.jetbrains.kotlin.config.LanguageFeature;
import org.jetbrains.kotlin.config.LanguageVersion;
import org.jetbrains.kotlin.config.LanguageVersionSettings;
@@ -23,10 +23,10 @@ import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.analyzer.moduleInfo
import org.jetbrains.kotlin.analyzer.unwrapPlatform
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.cfg.WhenMissingCase
import org.jetbrains.kotlin.cfg.hasUnknown
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.impl.TypeAliasConstructorDescriptor
import org.jetbrains.kotlin.diagnostics.WhenMissingCase
import org.jetbrains.kotlin.diagnostics.rendering.TabledDescriptorRenderer.newTable
import org.jetbrains.kotlin.diagnostics.rendering.TabledDescriptorRenderer.newText
import org.jetbrains.kotlin.name.FqName