Unify WhenMissingCase from FIR and FE1.0
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -12,12 +12,10 @@ import org.jetbrains.kotlin.builtins.UnsignedTypes;
|
||||
import org.jetbrains.kotlin.descriptors.*;
|
||||
import org.jetbrains.kotlin.descriptors.annotations.Annotated;
|
||||
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor;
|
||||
import org.jetbrains.kotlin.descriptors.impl.PackageFragmentDescriptorImpl;
|
||||
import org.jetbrains.kotlin.incremental.components.LookupLocation;
|
||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation;
|
||||
import org.jetbrains.kotlin.name.FqName;
|
||||
import org.jetbrains.kotlin.name.FqNameUnsafe;
|
||||
import org.jetbrains.kotlin.name.Name;
|
||||
import org.jetbrains.kotlin.name.SpecialNames;
|
||||
import org.jetbrains.kotlin.name.*;
|
||||
import org.jetbrains.kotlin.resolve.constants.ConstantValue;
|
||||
import org.jetbrains.kotlin.resolve.constants.StringValue;
|
||||
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter;
|
||||
@@ -114,6 +112,20 @@ public class DescriptorUtils {
|
||||
return getFqNameFromTopLevelClass(containingDeclaration).child(name);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static ClassId getClassIdForNonLocalClass(@NotNull DeclarationDescriptor descriptor) {
|
||||
DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration();
|
||||
Name name = descriptor.getName();
|
||||
if (containingDeclaration instanceof PackageFragmentDescriptorImpl) {
|
||||
FqName packageFqName = ((PackageFragmentDescriptorImpl) containingDeclaration).getFqName();
|
||||
return new ClassId(packageFqName, name);
|
||||
}
|
||||
if (!(containingDeclaration instanceof ClassDescriptor)) {
|
||||
return new ClassId(FqName.ROOT, name);
|
||||
}
|
||||
return getClassIdForNonLocalClass(containingDeclaration).createNestedClassId(name);
|
||||
}
|
||||
|
||||
public static boolean isTopLevelDeclaration(@Nullable DeclarationDescriptor descriptor) {
|
||||
return descriptor != null && descriptor.getContainingDeclaration() instanceof PackageFragmentDescriptor;
|
||||
}
|
||||
|
||||
-1
@@ -17,7 +17,6 @@ import org.jetbrains.kotlin.fir.declarations.FirCallableDeclaration
|
||||
import org.jetbrains.kotlin.fir.declarations.FirClass
|
||||
import org.jetbrains.kotlin.fir.declarations.FirMemberDeclaration
|
||||
import org.jetbrains.kotlin.fir.expressions.FirExpression
|
||||
import org.jetbrains.kotlin.fir.expressions.WhenMissingCase
|
||||
import org.jetbrains.kotlin.fir.symbols.AbstractFirBasedSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
|
||||
|
||||
+1
-1
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.idea.frontend.api.fir.diagnostics
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiTypeElement
|
||||
import org.jetbrains.kotlin.diagnostics.WhenMissingCase
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirPsiDiagnostic
|
||||
import org.jetbrains.kotlin.fir.declarations.FirCallableDeclaration
|
||||
@@ -14,7 +15,6 @@ import org.jetbrains.kotlin.fir.declarations.FirClass
|
||||
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
|
||||
import org.jetbrains.kotlin.fir.declarations.FirProperty
|
||||
import org.jetbrains.kotlin.fir.declarations.FirTypeParameter
|
||||
import org.jetbrains.kotlin.fir.expressions.WhenMissingCase
|
||||
import org.jetbrains.kotlin.fir.psi
|
||||
import org.jetbrains.kotlin.psi.KtClassOrObject
|
||||
import org.jetbrains.kotlin.psi.KtDeclaration
|
||||
|
||||
@@ -24,6 +24,7 @@ import org.jetbrains.kotlin.cfg.*
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||
import org.jetbrains.kotlin.diagnostics.WhenMissingCase
|
||||
import org.jetbrains.kotlin.idea.KotlinBundle
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||
import org.jetbrains.kotlin.idea.core.ShortenReferences
|
||||
@@ -89,14 +90,15 @@ class AddWhenRemainingBranchesFix(
|
||||
(whenCloseBrace.prevSibling as? PsiWhiteSpace)?.replace(psiFactory.createNewLine())
|
||||
for (case in missingCases) {
|
||||
val branchConditionText = when (case) {
|
||||
UnknownMissingCase, NullMissingCase, is BooleanMissingCase ->
|
||||
WhenMissingCase.Unknown, WhenMissingCase.NullIsMissing, is WhenMissingCase.BooleanIsMissing ->
|
||||
case.branchConditionText
|
||||
is ClassMissingCase ->
|
||||
if (case.classIsSingleton) {
|
||||
is WhenMissingCase.IsTypeCheckIsMissing ->
|
||||
if (case.isSingleton) {
|
||||
""
|
||||
} else {
|
||||
"is "
|
||||
} + case.descriptor.fqNameSafe.quoteIfNeeded().asString()
|
||||
} + case.classId.asSingleFqName().quoteIfNeeded().asString()
|
||||
is WhenMissingCase.EnumCheckIsMissing -> case.callableId.asFqNameForDebugInfo().quoteIfNeeded().asString()
|
||||
}
|
||||
val entry = psiFactory.createWhenEntry("$branchConditionText -> TODO()")
|
||||
if (elseBranch != null) {
|
||||
|
||||
Reference in New Issue
Block a user