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.ClassKind
|
||||||
import org.jetbrains.kotlin.descriptors.Modality
|
import org.jetbrains.kotlin.descriptors.Modality
|
||||||
import org.jetbrains.kotlin.diagnostics.Errors
|
import org.jetbrains.kotlin.diagnostics.Errors
|
||||||
|
import org.jetbrains.kotlin.diagnostics.WhenMissingCase
|
||||||
import org.jetbrains.kotlin.lexer.KtTokens
|
import org.jetbrains.kotlin.lexer.KtTokens
|
||||||
|
import org.jetbrains.kotlin.name.CallableId
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.checkReservedPrefixWord
|
import org.jetbrains.kotlin.psi.psiUtil.checkReservedPrefixWord
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext
|
import org.jetbrains.kotlin.resolve.BindingContext
|
||||||
@@ -40,19 +42,9 @@ import org.jetbrains.kotlin.types.KotlinType
|
|||||||
import org.jetbrains.kotlin.types.TypeUtils
|
import org.jetbrains.kotlin.types.TypeUtils
|
||||||
import java.util.*
|
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
|
val List<WhenMissingCase>.hasUnknown: Boolean
|
||||||
get() = firstOrNull() == UnknownMissingCase
|
get() = firstOrNull() == WhenMissingCase.Unknown
|
||||||
|
|
||||||
private interface WhenExhaustivenessChecker {
|
private interface WhenExhaustivenessChecker {
|
||||||
fun getMissingCases(
|
fun getMissingCases(
|
||||||
@@ -65,12 +57,6 @@ private interface WhenExhaustivenessChecker {
|
|||||||
fun isApplicable(subjectType: KotlinType): Boolean = false
|
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
|
// It's not a regular exhaustiveness checker, invoke it only inside other checkers
|
||||||
private object WhenOnNullableExhaustivenessChecker /* : WhenExhaustivenessChecker*/ {
|
private object WhenOnNullableExhaustivenessChecker /* : WhenExhaustivenessChecker*/ {
|
||||||
fun getMissingCases(expression: KtWhenExpression, context: BindingContext, nullable: Boolean) =
|
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 {
|
private object WhenOnBooleanExhaustivenessChecker : WhenExhaustivenessChecker {
|
||||||
override fun getMissingCases(
|
override fun getMissingCases(
|
||||||
@@ -118,8 +99,8 @@ private object WhenOnBooleanExhaustivenessChecker : WhenExhaustivenessChecker {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return (if (!containsTrue) listOf(BooleanMissingCase(true)) else listOf()) +
|
return (if (!containsTrue) listOf(WhenMissingCase.BooleanIsMissing.True) else listOf()) +
|
||||||
(if (!containsFalse) listOf(BooleanMissingCase(false)) else listOf()) +
|
(if (!containsFalse) listOf(WhenMissingCase.BooleanIsMissing.False) else listOf()) +
|
||||||
WhenOnNullableExhaustivenessChecker.getMissingCases(expression, context, nullable)
|
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 {
|
internal abstract class WhenOnClassExhaustivenessChecker : WhenExhaustivenessChecker {
|
||||||
private fun getReference(expression: KtExpression?): KtSimpleNameExpression? =
|
private fun getReference(expression: KtExpression?): KtSimpleNameExpression? =
|
||||||
@@ -195,7 +167,7 @@ internal abstract class WhenOnClassExhaustivenessChecker : WhenExhaustivenessChe
|
|||||||
context: BindingContext
|
context: BindingContext
|
||||||
): List<WhenMissingCase> {
|
): List<WhenMissingCase> {
|
||||||
// when on empty enum / sealed is considered non-exhaustive, see test whenOnEmptySealed
|
// 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>()
|
val checkedDescriptors = linkedSetOf<ClassDescriptor>()
|
||||||
for (whenEntry in whenExpression.entries) {
|
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)
|
) = WhenOnSealedExhaustivenessChecker.getMissingCases(expression, context, sealedClassDescriptor, false)
|
||||||
|
|
||||||
fun getMissingCases(expression: KtWhenExpression, context: BindingContext): List<WhenMissingCase> {
|
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 nullable = type.isMarkedNullable
|
||||||
val checkers = exhaustivenessCheckers.filter { it.isApplicable(type) }
|
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()
|
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 com.intellij.psi.impl.source.tree.LeafPsiElement;
|
||||||
import kotlin.Pair;
|
import kotlin.Pair;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.kotlin.cfg.WhenMissingCase;
|
|
||||||
import org.jetbrains.kotlin.config.LanguageFeature;
|
import org.jetbrains.kotlin.config.LanguageFeature;
|
||||||
import org.jetbrains.kotlin.config.LanguageVersion;
|
import org.jetbrains.kotlin.config.LanguageVersion;
|
||||||
import org.jetbrains.kotlin.config.LanguageVersionSettings;
|
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.moduleInfo
|
||||||
import org.jetbrains.kotlin.analyzer.unwrapPlatform
|
import org.jetbrains.kotlin.analyzer.unwrapPlatform
|
||||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||||
import org.jetbrains.kotlin.cfg.WhenMissingCase
|
|
||||||
import org.jetbrains.kotlin.cfg.hasUnknown
|
import org.jetbrains.kotlin.cfg.hasUnknown
|
||||||
import org.jetbrains.kotlin.descriptors.*
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
import org.jetbrains.kotlin.descriptors.impl.TypeAliasConstructorDescriptor
|
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.newTable
|
||||||
import org.jetbrains.kotlin.diagnostics.rendering.TabledDescriptorRenderer.newText
|
import org.jetbrains.kotlin.diagnostics.rendering.TabledDescriptorRenderer.newText
|
||||||
import org.jetbrains.kotlin.name.FqName
|
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.*;
|
||||||
import org.jetbrains.kotlin.descriptors.annotations.Annotated;
|
import org.jetbrains.kotlin.descriptors.annotations.Annotated;
|
||||||
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor;
|
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.LookupLocation;
|
||||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation;
|
import org.jetbrains.kotlin.incremental.components.NoLookupLocation;
|
||||||
import org.jetbrains.kotlin.name.FqName;
|
import org.jetbrains.kotlin.name.*;
|
||||||
import org.jetbrains.kotlin.name.FqNameUnsafe;
|
|
||||||
import org.jetbrains.kotlin.name.Name;
|
|
||||||
import org.jetbrains.kotlin.name.SpecialNames;
|
|
||||||
import org.jetbrains.kotlin.resolve.constants.ConstantValue;
|
import org.jetbrains.kotlin.resolve.constants.ConstantValue;
|
||||||
import org.jetbrains.kotlin.resolve.constants.StringValue;
|
import org.jetbrains.kotlin.resolve.constants.StringValue;
|
||||||
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter;
|
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter;
|
||||||
@@ -114,6 +112,20 @@ public class DescriptorUtils {
|
|||||||
return getFqNameFromTopLevelClass(containingDeclaration).child(name);
|
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) {
|
public static boolean isTopLevelDeclaration(@Nullable DeclarationDescriptor descriptor) {
|
||||||
return descriptor != null && descriptor.getContainingDeclaration() instanceof PackageFragmentDescriptor;
|
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.FirClass
|
||||||
import org.jetbrains.kotlin.fir.declarations.FirMemberDeclaration
|
import org.jetbrains.kotlin.fir.declarations.FirMemberDeclaration
|
||||||
import org.jetbrains.kotlin.fir.expressions.FirExpression
|
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.AbstractFirBasedSymbol
|
||||||
import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol
|
import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol
|
||||||
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
|
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.PsiElement
|
||||||
import com.intellij.psi.PsiTypeElement
|
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.FirErrors
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirPsiDiagnostic
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirPsiDiagnostic
|
||||||
import org.jetbrains.kotlin.fir.declarations.FirCallableDeclaration
|
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.FirDeclaration
|
||||||
import org.jetbrains.kotlin.fir.declarations.FirProperty
|
import org.jetbrains.kotlin.fir.declarations.FirProperty
|
||||||
import org.jetbrains.kotlin.fir.declarations.FirTypeParameter
|
import org.jetbrains.kotlin.fir.declarations.FirTypeParameter
|
||||||
import org.jetbrains.kotlin.fir.expressions.WhenMissingCase
|
|
||||||
import org.jetbrains.kotlin.fir.psi
|
import org.jetbrains.kotlin.fir.psi
|
||||||
import org.jetbrains.kotlin.psi.KtClassOrObject
|
import org.jetbrains.kotlin.psi.KtClassOrObject
|
||||||
import org.jetbrains.kotlin.psi.KtDeclaration
|
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.ClassDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||||
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||||
|
import org.jetbrains.kotlin.diagnostics.WhenMissingCase
|
||||||
import org.jetbrains.kotlin.idea.KotlinBundle
|
import org.jetbrains.kotlin.idea.KotlinBundle
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||||
import org.jetbrains.kotlin.idea.core.ShortenReferences
|
import org.jetbrains.kotlin.idea.core.ShortenReferences
|
||||||
@@ -89,14 +90,15 @@ class AddWhenRemainingBranchesFix(
|
|||||||
(whenCloseBrace.prevSibling as? PsiWhiteSpace)?.replace(psiFactory.createNewLine())
|
(whenCloseBrace.prevSibling as? PsiWhiteSpace)?.replace(psiFactory.createNewLine())
|
||||||
for (case in missingCases) {
|
for (case in missingCases) {
|
||||||
val branchConditionText = when (case) {
|
val branchConditionText = when (case) {
|
||||||
UnknownMissingCase, NullMissingCase, is BooleanMissingCase ->
|
WhenMissingCase.Unknown, WhenMissingCase.NullIsMissing, is WhenMissingCase.BooleanIsMissing ->
|
||||||
case.branchConditionText
|
case.branchConditionText
|
||||||
is ClassMissingCase ->
|
is WhenMissingCase.IsTypeCheckIsMissing ->
|
||||||
if (case.classIsSingleton) {
|
if (case.isSingleton) {
|
||||||
""
|
""
|
||||||
} else {
|
} else {
|
||||||
"is "
|
"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()")
|
val entry = psiFactory.createWhenEntry("$branchConditionText -> TODO()")
|
||||||
if (elseBranch != null) {
|
if (elseBranch != null) {
|
||||||
|
|||||||
Reference in New Issue
Block a user