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:
committed by
Mikhail Glukhikh
parent
6dc75c2e51
commit
c648356887
@@ -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
|
||||
}
|
||||
|
||||
+56
-11
@@ -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())
|
||||
|
||||
+16
-2
@@ -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
|
||||
|
||||
+7
-1
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user