FIR: report ENUM_ENTRY_AS_TYPE and IS_ENUM_TYPE

These two diagnostics are similar: both are reported on type references
to enum entries. But `IS_ENUM_TYPE` is reported if the type ref is an
operand of `is` operator. To pass along this contextual information, a
boolean is added to FirSpecificTypeResolverTransformer.
This commit is contained in:
Tianyu Geng
2021-06-08 15:14:41 -07:00
committed by Mikhail Glukhikh
parent 6dc75c2e51
commit c648356887
18 changed files with 157 additions and 45 deletions
@@ -966,6 +966,8 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") {
val USELESS_IS_CHECK by warning<KtElement> {
parameter<Boolean>("compileTimeCheckResult")
}
val IS_ENUM_ENTRY by error<KtTypeReference>()
val ENUM_ENTRY_AS_TYPE by error<KtTypeReference>()
}
val WHEN_EXPRESSIONS by object : DiagnosticGroup("When expressions") {
@@ -509,6 +509,8 @@ object FirErrors {
// Casts and is-checks
val USELESS_CAST by warning0<KtBinaryExpressionWithTypeRHS>(SourceElementPositioningStrategies.AS_TYPE)
val USELESS_IS_CHECK by warning1<KtElement, Boolean>()
val IS_ENUM_ENTRY by error0<KtTypeReference>()
val ENUM_ENTRY_AS_TYPE by error0<KtTypeReference>()
// When expressions
val EXPECTED_CONDITION by error0<KtWhenCondition>()
@@ -131,6 +131,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.DEPRECATION_ERROR
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.DESERIALIZATION_ERROR
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.DYNAMIC_UPPER_BOUND
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.EMPTY_RANGE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ENUM_ENTRY_AS_TYPE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.EQUALITY_NOT_APPLICABLE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.EQUALITY_NOT_APPLICABLE_WARNING
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ERROR_FROM_JAVA_RESOLUTION
@@ -223,6 +224,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INTERFACE_WITH_SU
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INVALID_IF_AS_EXPRESSION
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INVALID_TYPE_OF_ANNOTATION_MEMBER
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INVISIBLE_REFERENCE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.IS_ENUM_ENTRY
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ITERATOR_AMBIGUITY
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.KCLASS_WITH_NULLABLE_TYPE_PARAMETER_IN_SIGNATURE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.LEAKED_IN_PLACE_LAMBDA
@@ -1276,6 +1278,8 @@ class FirDefaultErrorMessages {
// Casts and is-checks
map.put(USELESS_CAST, "No cast needed")
map.put(USELESS_IS_CHECK, "Check for instance is always ''{0}''", TO_STRING)
map.put(IS_ENUM_ENTRY, "'is' over enum entry is not allowed, use comparison instead")
map.put(ENUM_ENTRY_AS_TYPE, "Use of enum entry names as types is not allowed, use enum type instead")
// When expressions
map.put(EXPECTED_CONDITION, "Expected condition of type Boolean")
@@ -359,6 +359,8 @@ private fun ConeSimpleDiagnostic.getFactory(source: FirSourceElement): FirDiagno
DiagnosticKind.RecursiveTypealiasExpansion -> FirErrors.RECURSIVE_TYPEALIAS_EXPANSION
DiagnosticKind.LoopInSupertype -> FirErrors.CYCLIC_INHERITANCE_HIERARCHY
DiagnosticKind.IllegalSelector -> FirErrors.ILLEGAL_SELECTOR
DiagnosticKind.IsEnumEntry -> FirErrors.IS_ENUM_ENTRY
DiagnosticKind.EnumEntryAsType -> FirErrors.ENUM_ENTRY_AS_TYPE
DiagnosticKind.UnresolvedSupertype,
DiagnosticKind.UnresolvedExpandedType,
DiagnosticKind.Other -> FirErrors.OTHER_ERROR
@@ -11,5 +11,5 @@ import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.fir.types.FirTypeRef
abstract class FirTypeResolver : FirSessionComponent {
abstract fun resolveType(typeRef: FirTypeRef, scopeClassDeclaration: ScopeClassDeclaration, areBareTypesAllowed: Boolean): ConeKotlinType
abstract fun resolveType(typeRef: FirTypeRef, scopeClassDeclaration: ScopeClassDeclaration, areBareTypesAllowed: Boolean, isOperandOfIsOperator: Boolean): ConeKotlinType
}
@@ -6,11 +6,18 @@
package org.jetbrains.kotlin.fir.resolve.providers.impl
import org.jetbrains.kotlin.builtins.StandardNames
import org.jetbrains.kotlin.fir.*
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.FirSourceElement
import org.jetbrains.kotlin.fir.ThreadSafeMutableState
import org.jetbrains.kotlin.fir.declarations.FirEnumEntry
import org.jetbrains.kotlin.fir.declarations.FirRegularClass
import org.jetbrains.kotlin.fir.declarations.impl.FirOuterClassTypeParameterRef
import org.jetbrains.kotlin.fir.declarations.utils.classId
import org.jetbrains.kotlin.fir.declarations.utils.isEnumClass
import org.jetbrains.kotlin.fir.diagnostics.ConeSimpleDiagnostic
import org.jetbrains.kotlin.fir.diagnostics.ConeUnexpectedTypeArgumentsError
import org.jetbrains.kotlin.fir.diagnostics.DiagnosticKind
import org.jetbrains.kotlin.fir.render
import org.jetbrains.kotlin.fir.resolve.*
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeOuterClassArgumentsRequired
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeUnresolvedQualifierError
@@ -20,6 +27,8 @@ import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
import org.jetbrains.kotlin.fir.resolve.transformers.ScopeClassDeclaration
import org.jetbrains.kotlin.fir.scopes.FirScope
import org.jetbrains.kotlin.fir.symbols.ConeTypeParameterLookupTag
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.SymbolInternals
import org.jetbrains.kotlin.fir.symbols.impl.*
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.fir.types.impl.ConeClassLikeTypeImpl
@@ -49,7 +58,7 @@ class FirTypeResolverImpl(private val session: FirSession) : FirTypeResolver() {
private fun resolveToSymbol(
typeRef: FirTypeRef,
scope: FirScope,
): Pair<FirClassifierSymbol<*>?, ConeSubstitutor?> {
): Pair<FirBasedSymbol<*>?, ConeSubstitutor?> {
return when (typeRef) {
is FirResolvedTypeRef -> {
val resultSymbol = typeRef.coneTypeSafe<ConeLookupTagBasedType>()?.lookupTag?.let(symbolProvider::getSymbolByLookupTag)
@@ -58,7 +67,7 @@ class FirTypeResolverImpl(private val session: FirSession) : FirTypeResolver() {
is FirUserTypeRef -> {
val qualifierResolver = session.qualifierResolver
var resolvedSymbol: FirClassifierSymbol<*>? = null
var resolvedSymbol: FirBasedSymbol<*>? = null
var substitutor: ConeSubstitutor? = null
scope.processClassifiersByNameWithSubstitution(typeRef.qualifier.first().name) { symbol, substitutorFromScope ->
if (resolvedSymbol != null) return@processClassifiersByNameWithSubstitution
@@ -68,6 +77,7 @@ class FirTypeResolverImpl(private val session: FirSession) : FirTypeResolver() {
symbol
} else {
qualifierResolver.resolveSymbolWithPrefix(typeRef.qualifier, symbol.classId)
?: qualifierResolver.resolveEnumEntrySymbol(typeRef.qualifier, symbol.classId)
}
}
is FirTypeParameterSymbol -> {
@@ -80,7 +90,7 @@ class FirTypeResolverImpl(private val session: FirSession) : FirTypeResolver() {
}
// TODO: Imports
val resultSymbol: FirClassifierSymbol<*>? = resolvedSymbol ?: qualifierResolver.resolveSymbol(typeRef.qualifier)
val resultSymbol: FirBasedSymbol<*>? = resolvedSymbol ?: qualifierResolver.resolveSymbol(typeRef.qualifier)
resultSymbol to substitutor
}
@@ -92,15 +102,40 @@ class FirTypeResolverImpl(private val session: FirSession) : FirTypeResolver() {
}
}
@OptIn(SymbolInternals::class)
private fun FirQualifierResolver.resolveEnumEntrySymbol(
qualifier: List<FirQualifierPart>,
classId: ClassId
): FirVariableSymbol<FirEnumEntry>? {
// Assuming the current qualifier refers to an enum entry, we drop the last part so we get a reference to the enum class.
val enumClassSymbol = resolveSymbolWithPrefix(qualifier.dropLast(1), classId) ?: return null
val enumClassFir = enumClassSymbol.fir as? FirRegularClass ?: return null
if (!enumClassFir.isEnumClass) return null
val enumEntryMatchingLastQualifier = enumClassFir.declarations
.firstOrNull { it is FirEnumEntry && it.name == qualifier.last().name } as? FirEnumEntry
return enumEntryMatchingLastQualifier?.symbol
}
@OptIn(SymbolInternals::class)
private fun resolveUserType(
typeRef: FirUserTypeRef,
symbol: FirClassifierSymbol<*>?,
symbol: FirBasedSymbol<*>?,
substitutor: ConeSubstitutor?,
areBareTypesAllowed: Boolean,
topDeclaration: FirRegularClass?
topDeclaration: FirRegularClass?,
isOperandOfIsOperator: Boolean
): ConeKotlinType {
if (symbol == null) {
return ConeKotlinErrorType(ConeUnresolvedQualifierError(typeRef.render()))
if (symbol == null || symbol !is FirClassifierSymbol<*>) {
val diagnostic = if (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)
}
} else {
ConeUnresolvedQualifierError(typeRef.render())
}
return ConeKotlinErrorType(diagnostic)
}
if (symbol is FirTypeParameterSymbol) {
for (part in typeRef.qualifier) {
@@ -191,6 +226,7 @@ class FirTypeResolverImpl(private val session: FirSession) : FirTypeResolver() {
}
}
@OptIn(SymbolInternals::class)
private fun getClassesAlignedToQualifierParts(
symbol: FirRegularClassSymbol,
qualifier: List<FirQualifierPart>,
@@ -243,6 +279,7 @@ class FirTypeResolverImpl(private val session: FirSession) : FirTypeResolver() {
val index: Int
)
@OptIn(SymbolInternals::class)
private fun createDiagnosticsIfExists(
parameterClass: FirRegularClass?,
qualifierPartIndex: Int,
@@ -314,13 +351,21 @@ class FirTypeResolverImpl(private val session: FirSession) : FirTypeResolver() {
override fun resolveType(
typeRef: FirTypeRef,
scopeClassDeclaration: ScopeClassDeclaration,
areBareTypesAllowed: Boolean
areBareTypesAllowed: Boolean,
isOperandOfIsOperator: Boolean
): ConeKotlinType {
return when (typeRef) {
is FirResolvedTypeRef -> typeRef.type
is FirUserTypeRef -> {
val (symbol, substitutor) = resolveToSymbol(typeRef, scopeClassDeclaration.scope)
resolveUserType(typeRef, symbol, substitutor, areBareTypesAllowed, scopeClassDeclaration.topDeclaration)
resolveUserType(
typeRef,
symbol,
substitutor,
areBareTypesAllowed,
scopeClassDeclaration.topDeclaration,
isOperandOfIsOperator
)
}
is FirFunctionTypeRef -> createFunctionalType(typeRef)
is FirDynamicTypeRef -> ConeKotlinErrorType(ConeUnsupportedDynamicType())
@@ -36,6 +36,20 @@ class FirSpecificTypeResolverTransformer(
}
}
@set:PrivateForInline
var isOperandOfIsOperator: Boolean = false
@OptIn(PrivateForInline::class)
inline fun <R> withIsOperandOfIsOperator(block: () -> R): R {
val oldValue = isOperandOfIsOperator
isOperandOfIsOperator = true
return try {
block()
} finally {
isOperandOfIsOperator = oldValue
}
}
@PrivateForInline
@JvmField
var currentFile: FirFile? = null
@@ -55,7 +69,7 @@ class FirSpecificTypeResolverTransformer(
override fun transformTypeRef(typeRef: FirTypeRef, data: ScopeClassDeclaration): FirResolvedTypeRef {
session.lookupTracker?.recordTypeLookup(typeRef, data.scope.scopeOwnerLookupNames, currentFile?.source)
typeRef.transformChildren(this, data)
return transformType(typeRef, typeResolver.resolveType(typeRef, data, areBareTypesAllowed))
return transformType(typeRef, typeResolver.resolveType(typeRef, data, areBareTypesAllowed, isOperandOfIsOperator))
}
@OptIn(PrivateForInline::class)
@@ -65,7 +79,7 @@ class FirSpecificTypeResolverTransformer(
): FirResolvedTypeRef {
functionTypeRef.transformChildren(this, data)
session.lookupTracker?.recordTypeLookup(functionTypeRef, data.scope.scopeOwnerLookupNames, currentFile?.source)
val resolvedType = typeResolver.resolveType(functionTypeRef, data, areBareTypesAllowed).takeIfAcceptable()
val resolvedType = typeResolver.resolveType(functionTypeRef, data, areBareTypesAllowed, isOperandOfIsOperator).takeIfAcceptable()
return if (resolvedType != null && resolvedType !is ConeClassErrorType) {
buildResolvedTypeRef {
source = functionTypeRef.source
@@ -606,7 +606,13 @@ open class FirExpressionsResolveTransformer(transformer: FirBodyResolveTransform
data: ResolutionMode,
): FirStatement {
val resolved = components.typeResolverTransformer.withAllowedBareTypes {
typeOperatorCall.transformConversionTypeRef(transformer, ResolutionMode.ContextIndependent)
if (typeOperatorCall.operation == FirOperation.IS || typeOperatorCall.operation == FirOperation.NOT_IS) {
components.typeResolverTransformer.withIsOperandOfIsOperator {
typeOperatorCall.transformConversionTypeRef(transformer, ResolutionMode.ContextIndependent)
}
} else {
typeOperatorCall.transformConversionTypeRef(transformer, ResolutionMode.ContextIndependent)
}
}.transformTypeOperatorCallChildren()
val conversionTypeRef = resolved.conversionTypeRef.withTypeArgumentsForBareType(resolved.argument)
@@ -48,5 +48,8 @@ enum class DiagnosticKind {
FloatLiteralOutOfRange,
WrongLongSuffix,
IsEnumEntry,
EnumEntryAsType,
Other,
}
+22 -22
View File
@@ -6,49 +6,49 @@ enum class Color {
}
}
class MyColor(val x: <!UNRESOLVED_REFERENCE!>Color.RED<!>, y: <!UNRESOLVED_REFERENCE!>Color.RED<!>) : <!UNRESOLVED_REFERENCE!>Color.RED<!> {
class MyColor(val x: <!ENUM_ENTRY_AS_TYPE!>Color.RED<!>, y: <!ENUM_ENTRY_AS_TYPE!>Color.RED<!>) : <!ENUM_ENTRY_AS_TYPE!>Color.RED<!> {
var z: <!UNRESOLVED_REFERENCE!>Color.RED<!> = Color.RED
set(arg: <!UNRESOLVED_REFERENCE!>Color.RED<!>) { z = arg }
var z: <!ENUM_ENTRY_AS_TYPE!>Color.RED<!> = Color.RED
set(arg: <!ENUM_ENTRY_AS_TYPE!>Color.RED<!>) { z = arg }
fun foo(arg: <!UNRESOLVED_REFERENCE!>Color.RED<!>): <!UNRESOLVED_REFERENCE!>Color.RED<!> = arg
fun foo(arg: <!ENUM_ENTRY_AS_TYPE!>Color.RED<!>): <!ENUM_ENTRY_AS_TYPE!>Color.RED<!> = arg
fun bar(): <!UNRESOLVED_REFERENCE!>Color.RED<!> {
class Local : <!UNRESOLVED_REFERENCE!>Color.RED<!>
fun local(arg: <!UNRESOLVED_REFERENCE!>Color.RED<!>): <!UNRESOLVED_REFERENCE!>Color.RED<!> = arg
val temp: <!UNRESOLVED_REFERENCE!>Color.RED<!> = Color.RED
temp as? <!UNRESOLVED_REFERENCE, UNRESOLVED_REFERENCE!>Color.RED<!>
if (temp is <!UNRESOLVED_REFERENCE!>Color.RED<!>) {
return temp as <!UNRESOLVED_REFERENCE!>Color.RED<!>
fun bar(): <!ENUM_ENTRY_AS_TYPE!>Color.RED<!> {
class Local : <!ENUM_ENTRY_AS_TYPE!>Color.RED<!>
fun local(arg: <!ENUM_ENTRY_AS_TYPE!>Color.RED<!>): <!ENUM_ENTRY_AS_TYPE!>Color.RED<!> = arg
val temp: <!ENUM_ENTRY_AS_TYPE!>Color.RED<!> = Color.RED
temp as? <!ENUM_ENTRY_AS_TYPE, ENUM_ENTRY_AS_TYPE!>Color.RED<!>
if (temp is <!IS_ENUM_ENTRY!>Color.RED<!>) {
return temp as <!ENUM_ENTRY_AS_TYPE!>Color.RED<!>
}
val obj = object : <!UNRESOLVED_REFERENCE!>Color.RED<!> {}
if (obj is <!UNRESOLVED_REFERENCE!>Color.RED<!>) {
val obj = object : <!ENUM_ENTRY_AS_TYPE!>Color.RED<!> {}
if (obj is <!IS_ENUM_ENTRY!>Color.RED<!>) {
return obj
}
return Color.RED
}
}
fun create(): Array<<!UNRESOLVED_REFERENCE!>Color.RED<!>>? = null
fun create(): Array<<!ENUM_ENTRY_AS_TYPE!>Color.RED<!>>? = null
interface Your<T : <!UNRESOLVED_REFERENCE!>Color.RED<!>>
interface Your<T : <!ENUM_ENTRY_AS_TYPE!>Color.RED<!>>
class His : Your<<!UNRESOLVED_REFERENCE!>Color.RED<!>>
class His : Your<<!ENUM_ENTRY_AS_TYPE!>Color.RED<!>>
fun <T : <!UNRESOLVED_REFERENCE!>Color.RED<!>> otherCreate(): Array<T>? = null
fun <T : <!ENUM_ENTRY_AS_TYPE!>Color.RED<!>> otherCreate(): Array<T>? = null
typealias RedAlias = <!UNRESOLVED_REFERENCE!>Color.RED<!>
typealias RedAlias = <!ENUM_ENTRY_AS_TYPE!>Color.RED<!>
typealias ArrayOfEnumEntry = Array<<!UNRESOLVED_REFERENCE!>Color.RED<!>>
typealias ArrayOfEnumEntry = Array<<!ENUM_ENTRY_AS_TYPE!>Color.RED<!>>
typealias ArrayOfEnumEntryAlias = Array<RedAlias>
fun <T> bar(a: Any): T = a as T
fun <T> foo() {
foo<<!UNRESOLVED_REFERENCE!>Color.RED<!>>()
foo<<!ENUM_ENTRY_AS_TYPE!>Color.RED<!>>()
foo<<!CANNOT_INFER_PARAMETER_TYPE!>RedAlias<!>>()
bar<<!UNRESOLVED_REFERENCE!>Color.RED<!>>(<!ARGUMENT_TYPE_MISMATCH!>Color.RED<!>)
bar<<!ENUM_ENTRY_AS_TYPE!>Color.RED<!>>(<!ARGUMENT_TYPE_MISMATCH!>Color.RED<!>)
}
fun Array<<!UNRESOLVED_REFERENCE!>Color.RED<!>>.foo(entries: Array<<!UNRESOLVED_REFERENCE!>Color.RED<!>>): Array<<!UNRESOLVED_REFERENCE!>Color.RED<!>> = null!!
fun Array<<!ENUM_ENTRY_AS_TYPE!>Color.RED<!>>.foo(entries: Array<<!ENUM_ENTRY_AS_TYPE!>Color.RED<!>>): Array<<!ENUM_ENTRY_AS_TYPE!>Color.RED<!>> = null!!
@@ -3,4 +3,4 @@ enum class MyEnum {
SECOND
}
fun foo(me: MyEnum): Boolean = if (me is <!UNRESOLVED_REFERENCE!>MyEnum.FIRST<!>) true else false
fun foo(me: MyEnum): Boolean = if (me is <!IS_ENUM_ENTRY!>MyEnum.FIRST<!>) true else false
@@ -2,4 +2,4 @@ enum class E {
ENTRY
}
class A : <!UNRESOLVED_REFERENCE!>E.ENTRY<!>
class A : <!ENUM_ENTRY_AS_TYPE!>E.ENTRY<!>
@@ -3,4 +3,4 @@ enum class MyEnum {
SECOND
}
fun foo(me: MyEnum): Boolean = me is <!UNRESOLVED_REFERENCE!>MyEnum.FIRST<!>
fun foo(me: MyEnum): Boolean = me is <!IS_ENUM_ENTRY!>MyEnum.FIRST<!>
@@ -13,8 +13,8 @@ enum class MyEnum {
fun foo(x: MyEnum): Int {
return <!NO_ELSE_IN_WHEN!>when<!> (x) {
is <!UNRESOLVED_REFERENCE!>MyEnum.A<!> -> 1
is <!UNRESOLVED_REFERENCE!>MyEnum.B<!> -> 2
is <!UNRESOLVED_REFERENCE!>MyEnum.C<!> -> 3
is <!IS_ENUM_ENTRY!>MyEnum.A<!> -> 1
is <!IS_ENUM_ENTRY!>MyEnum.B<!> -> 2
is <!IS_ENUM_ENTRY!>MyEnum.C<!> -> 3
}
}
@@ -15,7 +15,7 @@ enum class MyEnum {
fun foo(x: MyEnum): Int {
return <!NO_ELSE_IN_WHEN!>when<!> (x) {
MyEnum.A -> 1
is <!UNRESOLVED_REFERENCE!>MyEnum.B<!> -> 2
is <!UNRESOLVED_REFERENCE!>MyEnum.C<!> -> 3
is <!IS_ENUM_ENTRY!>MyEnum.B<!> -> 2
is <!IS_ENUM_ENTRY!>MyEnum.C<!> -> 3
}
}
@@ -2616,6 +2616,18 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert
token,
)
}
add(FirErrors.IS_ENUM_ENTRY) { firDiagnostic ->
IsEnumEntryImpl(
firDiagnostic as FirPsiDiagnostic,
token,
)
}
add(FirErrors.ENUM_ENTRY_AS_TYPE) { firDiagnostic ->
EnumEntryAsTypeImpl(
firDiagnostic as FirPsiDiagnostic,
token,
)
}
add(FirErrors.EXPECTED_CONDITION) { firDiagnostic ->
ExpectedConditionImpl(
firDiagnostic as FirPsiDiagnostic,
@@ -1834,6 +1834,14 @@ sealed class KtFirDiagnostic<PSI : PsiElement> : KtDiagnosticWithPsi<PSI> {
abstract val compileTimeCheckResult: Boolean
}
abstract class IsEnumEntry : KtFirDiagnostic<KtTypeReference>() {
override val diagnosticClass get() = IsEnumEntry::class
}
abstract class EnumEntryAsType : KtFirDiagnostic<KtTypeReference>() {
override val diagnosticClass get() = EnumEntryAsType::class
}
abstract class ExpectedCondition : KtFirDiagnostic<KtWhenCondition>() {
override val diagnosticClass get() = ExpectedCondition::class
}
@@ -2954,6 +2954,20 @@ internal class UselessIsCheckImpl(
override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic)
}
internal class IsEnumEntryImpl(
firDiagnostic: FirPsiDiagnostic,
override val token: ValidityToken,
) : KtFirDiagnostic.IsEnumEntry(), KtAbstractFirDiagnostic<KtTypeReference> {
override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic)
}
internal class EnumEntryAsTypeImpl(
firDiagnostic: FirPsiDiagnostic,
override val token: ValidityToken,
) : KtFirDiagnostic.EnumEntryAsType(), KtAbstractFirDiagnostic<KtTypeReference> {
override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic)
}
internal class ExpectedConditionImpl(
firDiagnostic: FirPsiDiagnostic,
override val token: ValidityToken,