K2: expand type before isSomeType checks properly

Related to KT-60229
This commit is contained in:
Mikhail Glukhikh
2023-07-13 14:27:48 +02:00
committed by Space Team
parent 51e8a72f47
commit 00b4ae6ae9
30 changed files with 140 additions and 73 deletions
@@ -33,6 +33,7 @@ import org.jetbrains.kotlin.fir.psi
import org.jetbrains.kotlin.fir.references.* import org.jetbrains.kotlin.fir.references.*
import org.jetbrains.kotlin.fir.resolve.ScopeSession import org.jetbrains.kotlin.fir.resolve.ScopeSession
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeUnmatchedTypeArgumentsError import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeUnmatchedTypeArgumentsError
import org.jetbrains.kotlin.fir.resolve.fullyExpandedType
import org.jetbrains.kotlin.fir.resolve.providers.toSymbol import org.jetbrains.kotlin.fir.resolve.providers.toSymbol
import org.jetbrains.kotlin.fir.resolve.scope import org.jetbrains.kotlin.fir.resolve.scope
import org.jetbrains.kotlin.fir.resolve.toSymbol import org.jetbrains.kotlin.fir.resolve.toSymbol
@@ -261,7 +262,7 @@ internal object FirReferenceResolveHelper {
return buildList { return buildList {
scope.processFunctionsByName(OperatorNameConventions.EQUALS) { functionSymbol -> scope.processFunctionsByName(OperatorNameConventions.EQUALS) { functionSymbol ->
val parameterSymbol = functionSymbol.valueParameterSymbols.singleOrNull() val parameterSymbol = functionSymbol.valueParameterSymbols.singleOrNull()
if (parameterSymbol != null && parameterSymbol.fir.returnTypeRef.isNullableAny) { if (parameterSymbol != null && parameterSymbol.fir.returnTypeRef.coneType.fullyExpandedType(session).isNullableAny) {
add(functionSymbol.buildSymbol(symbolBuilder)) add(functionSymbol.buildSymbol(symbolBuilder))
} }
} }
@@ -19,6 +19,7 @@ import org.jetbrains.kotlin.analysis.api.types.KtType
import org.jetbrains.kotlin.fir.symbols.impl.FirTypeParameterSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirTypeParameterSymbol
import org.jetbrains.kotlin.fir.types.isNullableAny import org.jetbrains.kotlin.fir.types.isNullableAny
import org.jetbrains.kotlin.fir.analysis.checkers.typeParameterSymbols import org.jetbrains.kotlin.fir.analysis.checkers.typeParameterSymbols
import org.jetbrains.kotlin.fir.types.UnexpandedTypeCheck
/** /**
* [KtFirTypeParameterSymbolBase] provides shared implementations for [KtFirTypeParameterSymbol] and [KtFirPsiJavaTypeParameterSymbol]. * [KtFirTypeParameterSymbolBase] provides shared implementations for [KtFirTypeParameterSymbol] and [KtFirPsiJavaTypeParameterSymbol].
@@ -29,6 +30,7 @@ internal sealed class KtFirTypeParameterSymbolBase : KtTypeParameterSymbol(), Kt
KtFirAnnotationListForDeclaration.create(firSymbol, analysisSession.useSiteSession, token) KtFirAnnotationListForDeclaration.create(firSymbol, analysisSession.useSiteSession, token)
} }
@OptIn(UnexpandedTypeCheck::class)
override val upperBounds: List<KtType> by cached { override val upperBounds: List<KtType> by cached {
firSymbol.resolvedBounds.mapNotNull { type -> firSymbol.resolvedBounds.mapNotNull { type ->
if (type.isNullableAny) return@mapNotNull null if (type.isNullableAny) return@mapNotNull null
@@ -40,7 +40,6 @@ import org.jetbrains.kotlin.fir.types.isArrayType
import org.jetbrains.kotlin.fir.types.isString import org.jetbrains.kotlin.fir.types.isString
import org.jetbrains.kotlin.fir.visitors.FirVisitorVoid import org.jetbrains.kotlin.fir.visitors.FirVisitorVoid
import org.jetbrains.kotlin.ir.backend.jvm.serialization.JvmIrMangler import org.jetbrains.kotlin.ir.backend.jvm.serialization.JvmIrMangler
import org.jetbrains.kotlin.ir.symbols.*
import org.jetbrains.kotlin.load.kotlin.incremental.components.IncrementalCompilationComponents import org.jetbrains.kotlin.load.kotlin.incremental.components.IncrementalCompilationComponents
import org.jetbrains.kotlin.modules.Module import org.jetbrains.kotlin.modules.Module
import org.jetbrains.kotlin.modules.TargetId import org.jetbrains.kotlin.modules.TargetId
@@ -165,7 +165,7 @@ object FirRepeatableAnnotationChecker : FirBasicDeclarationChecker() {
val valueParameterSymbols = containerCtor.valueParameterSymbols val valueParameterSymbols = containerCtor.valueParameterSymbols
val parameterName = StandardClassIds.Annotations.ParameterNames.value val parameterName = StandardClassIds.Annotations.ParameterNames.value
val value = valueParameterSymbols.find { it.name == parameterName } val value = valueParameterSymbols.find { it.name == parameterName }
if (value == null || !value.resolvedReturnTypeRef.isArrayType || if (value == null || !value.resolvedReturnTypeRef.coneType.fullyExpandedType(context.session).isArrayType ||
value.resolvedReturnTypeRef.type.typeArguments.single().type != annotationClass.defaultType() value.resolvedReturnTypeRef.type.typeArguments.single().type != annotationClass.defaultType()
) { ) {
reporter.reportOn( reporter.reportOn(
@@ -13,10 +13,10 @@ import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
import org.jetbrains.kotlin.fir.declarations.FirRegularClass import org.jetbrains.kotlin.fir.declarations.FirRegularClass
import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction
import org.jetbrains.kotlin.fir.declarations.isEquals
import org.jetbrains.kotlin.fir.declarations.utils.hasBody import org.jetbrains.kotlin.fir.declarations.utils.hasBody
import org.jetbrains.kotlin.fir.declarations.utils.isInterface import org.jetbrains.kotlin.fir.declarations.utils.isInterface
import org.jetbrains.kotlin.fir.declarations.utils.isOverride import org.jetbrains.kotlin.fir.declarations.utils.isOverride
import org.jetbrains.kotlin.fir.symbols.impl.isEquals
import org.jetbrains.kotlin.name.CallableId import org.jetbrains.kotlin.name.CallableId
import org.jetbrains.kotlin.util.OperatorNameConventions.TO_STRING import org.jetbrains.kotlin.util.OperatorNameConventions.TO_STRING
@@ -43,7 +43,7 @@ object FirMethodOfAnyImplementedInInterfaceChecker : FirRegularClassChecker(), F
(function.name == HASHCODE_NAME || function.name == TO_STRING) (function.name == HASHCODE_NAME || function.name == TO_STRING)
) { ) {
methodOfAny = true methodOfAny = true
} else if (function.isEquals()) { } else if (function.isEquals(context.session)) {
methodOfAny = true methodOfAny = true
} }
@@ -24,7 +24,6 @@ import org.jetbrains.kotlin.fir.resolve.defaultType
import org.jetbrains.kotlin.fir.resolve.fullyExpandedType import org.jetbrains.kotlin.fir.resolve.fullyExpandedType
import org.jetbrains.kotlin.fir.resolve.lookupSuperTypes import org.jetbrains.kotlin.fir.resolve.lookupSuperTypes
import org.jetbrains.kotlin.fir.symbols.impl.FirValueParameterSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirValueParameterSymbol
import org.jetbrains.kotlin.fir.symbols.impl.isEquals
import org.jetbrains.kotlin.fir.types.* import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.fir.types.impl.FirImplicitAnyTypeRef import org.jetbrains.kotlin.fir.types.impl.FirImplicitAnyTypeRef
import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.lexer.KtTokens
@@ -192,7 +191,7 @@ object FirValueClassDeclarationChecker : FirRegularClassChecker() {
) )
} }
primaryConstructorParameter.returnTypeRef.isInapplicableParameterType() -> { primaryConstructorParameter.returnTypeRef.isInapplicableParameterType(context.session) -> {
reporter.reportOn( reporter.reportOn(
primaryConstructorParameter.returnTypeRef.source, primaryConstructorParameter.returnTypeRef.source,
FirErrors.VALUE_CLASS_HAS_INAPPLICABLE_PARAMETER_TYPE, FirErrors.VALUE_CLASS_HAS_INAPPLICABLE_PARAMETER_TYPE,
@@ -227,7 +226,7 @@ object FirValueClassDeclarationChecker : FirRegularClassChecker() {
if (it !is FirSimpleFunction) { if (it !is FirSimpleFunction) {
return@forEach return@forEach
} }
if (it.isEquals()) equalsFromAnyOverriding = it if (it.isEquals(context.session)) equalsFromAnyOverriding = it
if (it.isTypedEqualsInValueClass(context.session)) typedEquals = it if (it.isTypedEqualsInValueClass(context.session)) typedEquals = it
} }
equalsFromAnyOverriding to typedEquals equalsFromAnyOverriding to typedEquals
@@ -268,8 +267,8 @@ object FirValueClassDeclarationChecker : FirRegularClassChecker() {
return isVararg || !primaryConstructorProperty.isVal || isOpen return isVararg || !primaryConstructorProperty.isVal || isOpen
} }
private fun FirTypeRef.isInapplicableParameterType() = private fun FirTypeRef.isInapplicableParameterType(session: FirSession): Boolean =
isUnit || isNothing coneType.fullyExpandedType(session).let { it.isUnit || it.isNothing }
private fun ConeKotlinType.isGenericArrayOfTypeParameter(): Boolean { private fun ConeKotlinType.isGenericArrayOfTypeParameter(): Boolean {
if (this.typeArguments.firstOrNull() is ConeStarProjection || !isPotentiallyArray()) if (this.typeArguments.firstOrNull() is ConeStarProjection || !isPotentiallyArray())
@@ -20,6 +20,7 @@ import org.jetbrains.kotlin.fir.languageVersionSettings
import org.jetbrains.kotlin.fir.references.FirErrorNamedReference import org.jetbrains.kotlin.fir.references.FirErrorNamedReference
import org.jetbrains.kotlin.fir.types.ConeKotlinType import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.fir.types.FirErrorTypeRef import org.jetbrains.kotlin.fir.types.FirErrorTypeRef
import org.jetbrains.kotlin.fir.types.UnexpandedTypeCheck
import org.jetbrains.kotlin.fir.types.coneType import org.jetbrains.kotlin.fir.types.coneType
import org.jetbrains.kotlin.fir.types.isArrayType import org.jetbrains.kotlin.fir.types.isArrayType
@@ -50,6 +51,8 @@ object FirNamedVarargChecker : FirCallChecker() {
val typeRef = argument.expression.typeRef val typeRef = argument.expression.typeRef
if (typeRef is FirErrorTypeRef) return if (typeRef is FirErrorTypeRef) return
if (argument.expression is FirArrayOfCall) return if (argument.expression is FirArrayOfCall) return
@OptIn(UnexpandedTypeCheck::class)
if (allowAssignArray && typeRef.isArrayType) return if (allowAssignArray && typeRef.isArrayType) return
if (isAnnotation) { if (isAnnotation) {
@@ -30,6 +30,7 @@ object FirNotNullAssertionChecker : FirCheckNotNullCallChecker() {
} }
// TODO: use of Unit is subject to change. // TODO: use of Unit is subject to change.
// See BodyResolveComponents.typeForQualifier in ResolveUtils.kt which returns Unit for no value type. // See BodyResolveComponents.typeForQualifier in ResolveUtils.kt which returns Unit for no value type.
@OptIn(UnexpandedTypeCheck::class)
if (argument is FirResolvedQualifier && argument.typeRef.isUnit) { if (argument is FirResolvedQualifier && argument.typeRef.isUnit) {
// Would be reported as NO_COMPANION_OBJECT // Would be reported as NO_COMPANION_OBJECT
return return
@@ -17,6 +17,7 @@ import org.jetbrains.kotlin.fir.expressions.FirResolvedQualifier
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirTypeAliasSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirTypeAliasSymbol
import org.jetbrains.kotlin.fir.types.UnexpandedTypeCheck
import org.jetbrains.kotlin.fir.types.isUnit import org.jetbrains.kotlin.fir.types.isUnit
object FirStandaloneQualifierChecker : FirResolvedQualifierChecker() { object FirStandaloneQualifierChecker : FirResolvedQualifierChecker() {
@@ -26,8 +27,11 @@ object FirStandaloneQualifierChecker : FirResolvedQualifierChecker() {
if (lastQualifiedAccess?.explicitReceiver === expression || lastQualifiedAccess?.dispatchReceiver === expression) return if (lastQualifiedAccess?.explicitReceiver === expression || lastQualifiedAccess?.dispatchReceiver === expression) return
val lastGetClass = context.getClassCalls.lastOrNull() val lastGetClass = context.getClassCalls.lastOrNull()
if (lastGetClass?.argument === expression) return if (lastGetClass?.argument === expression) return
// Note: if it's real Unit, it will be filtered by ClassKind.OBJECT check below
// Note: if it's real Unit, it will be filtered by ClassKind.OBJECT check below in reportErrorOn
@OptIn(UnexpandedTypeCheck::class)
if (!expression.typeRef.isUnit) return if (!expression.typeRef.isUnit) return
expression.symbol.reportErrorOn(expression.source, context, reporter) expression.symbol.reportErrorOn(expression.source, context, reporter)
} }
@@ -13,6 +13,8 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
import org.jetbrains.kotlin.diagnostics.reportOn import org.jetbrains.kotlin.diagnostics.reportOn
import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction
import org.jetbrains.kotlin.fir.expressions.impl.FirSingleExpressionBlock import org.jetbrains.kotlin.fir.expressions.impl.FirSingleExpressionBlock
import org.jetbrains.kotlin.fir.resolve.fullyExpandedType
import org.jetbrains.kotlin.fir.types.coneType
import org.jetbrains.kotlin.fir.types.isUnit import org.jetbrains.kotlin.fir.types.isUnit
object RedundantReturnUnitType : FirSimpleFunctionChecker() { object RedundantReturnUnitType : FirSimpleFunctionChecker() {
@@ -23,7 +25,7 @@ object RedundantReturnUnitType : FirSimpleFunctionChecker() {
if (declaration.source?.kind is KtFakeSourceElementKind) return if (declaration.source?.kind is KtFakeSourceElementKind) return
if (returnType.annotations.isNotEmpty()) return if (returnType.annotations.isNotEmpty()) return
if (returnType.isUnit) { if (returnType.coneType.fullyExpandedType(context.session).isUnit) {
reporter.reportOn(declaration.returnTypeRef.source, FirErrors.REDUNDANT_RETURN_UNIT_TYPE, context) reporter.reportOn(declaration.returnTypeRef.source, FirErrors.REDUNDANT_RETURN_UNIT_TYPE, context)
} }
} }
@@ -45,7 +45,8 @@ val ConeKotlinType.isPrimitiveNumberOrNullableType: Boolean
val ConeKotlinType.isArrayType: Boolean val ConeKotlinType.isArrayType: Boolean
get() { get() {
return isBuiltinType(StandardClassIds.Array, false) || return isBuiltinType(StandardClassIds.Array, false) ||
StandardClassIds.primitiveArrayTypeByElementType.values.any { isBuiltinType(it, false) } StandardClassIds.primitiveArrayTypeByElementType.values.any { isBuiltinType(it, false) } ||
StandardClassIds.unsignedArrayTypeByElementType.values.any { isBuiltinType(it, false) }
} }
// Same as [KotlinBuiltIns#isNonPrimitiveArray] // Same as [KotlinBuiltIns#isNonPrimitiveArray]
@@ -16,6 +16,7 @@ import org.jetbrains.kotlin.fir.declarations.FirContractDescriptionOwner
import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction
import org.jetbrains.kotlin.fir.diagnostics.ConeDiagnostic import org.jetbrains.kotlin.fir.diagnostics.ConeDiagnostic
import org.jetbrains.kotlin.fir.types.ConeKotlinType import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.fir.types.UnexpandedTypeCheck
import org.jetbrains.kotlin.fir.types.isBoolean import org.jetbrains.kotlin.fir.types.isBoolean
import org.jetbrains.kotlin.metadata.ProtoBuf import org.jetbrains.kotlin.metadata.ProtoBuf
import org.jetbrains.kotlin.metadata.deserialization.isInstanceType import org.jetbrains.kotlin.metadata.deserialization.isInstanceType
@@ -37,7 +38,7 @@ class FirContractDeserializer(private val c: FirDeserializationContext) :
override fun extractVariable( override fun extractVariable(
valueParameterIndex: Int, valueParameterIndex: Int,
owner: FirContractDescriptionOwner owner: FirContractDescriptionOwner
): KtValueParameterReference<ConeKotlinType, ConeDiagnostic>? { ): KtValueParameterReference<ConeKotlinType, ConeDiagnostic>? {
val name: String val name: String
val ownerFunction = owner as FirSimpleFunction val ownerFunction = owner as FirSimpleFunction
val typeRef = if (valueParameterIndex < 0) { val typeRef = if (valueParameterIndex < 0) {
@@ -49,6 +50,7 @@ class FirContractDeserializer(private val c: FirDeserializationContext) :
parameter.returnTypeRef parameter.returnTypeRef
} ?: return null } ?: return null
@OptIn(UnexpandedTypeCheck::class)
return if (!typeRef.isBoolean) return if (!typeRef.isBoolean)
KtValueParameterReference(valueParameterIndex, name) KtValueParameterReference(valueParameterIndex, name)
else else
@@ -196,8 +196,8 @@ internal object FirToConstantValueTransformer : FirDefaultVisitor<ConstantValue<
functionCall: FirFunctionCall, functionCall: FirFunctionCall,
data: FirToConstantValueTransformerData data: FirToConstantValueTransformerData
): ConstantValue<*>? { ): ConstantValue<*>? {
if (functionCall.isArrayOfCall) { if (functionCall.isArrayOfCall(data.session)) {
return FirArrayOfCallTransformer().transformFunctionCall(functionCall, null).accept(this, data) return FirArrayOfCallTransformer().transformFunctionCall(functionCall, data.session).accept(this, data)
} }
return visitQualifiedAccessExpression(functionCall, data) return visitQualifiedAccessExpression(functionCall, data)
} }
@@ -289,7 +289,7 @@ internal object FirToConstantValueChecker : FirDefaultVisitor<Boolean, FirSessio
} }
override fun visitFunctionCall(functionCall: FirFunctionCall, data: FirSession): Boolean { override fun visitFunctionCall(functionCall: FirFunctionCall, data: FirSession): Boolean {
if (functionCall.isArrayOfCall) return functionCall.arguments.all { it.accept(this, data) } if (functionCall.isArrayOfCall(data)) return functionCall.arguments.all { it.accept(this, data) }
return visitQualifiedAccessExpression(functionCall, data) return visitQualifiedAccessExpression(functionCall, data)
} }
@@ -364,6 +364,7 @@ internal class ClassMemberGenerator(
* constructor() : this(10) // <---- this call, IrDelegatingConstructorCall * constructor() : this(10) // <---- this call, IrDelegatingConstructorCall
* } * }
*/ */
@OptIn(UnexpandedTypeCheck::class)
if ((constructor.isFromEnumClass || constructor.returnTypeRef.isEnum) && this.isSuper) { if ((constructor.isFromEnumClass || constructor.returnTypeRef.isEnum) && this.isSuper) {
IrEnumConstructorCallImpl( IrEnumConstructorCallImpl(
startOffset, endOffset, startOffset, endOffset,
@@ -13,6 +13,7 @@ import org.jetbrains.kotlin.fir.declarations.utils.isSuspend
import org.jetbrains.kotlin.fir.languageVersionSettings import org.jetbrains.kotlin.fir.languageVersionSettings
import org.jetbrains.kotlin.fir.resolve.ScopeSession import org.jetbrains.kotlin.fir.resolve.ScopeSession
import org.jetbrains.kotlin.fir.resolve.defaultType import org.jetbrains.kotlin.fir.resolve.defaultType
import org.jetbrains.kotlin.fir.resolve.fullyExpandedType
import org.jetbrains.kotlin.fir.resolve.toFirRegularClassSymbol import org.jetbrains.kotlin.fir.resolve.toFirRegularClassSymbol
import org.jetbrains.kotlin.fir.scopes.overriddenFunctions import org.jetbrains.kotlin.fir.scopes.overriddenFunctions
import org.jetbrains.kotlin.fir.types.* import org.jetbrains.kotlin.fir.types.*
@@ -45,7 +46,7 @@ object OperatorFunctionChecks {
checkFor( checkFor(
OperatorNameConventions.SET, OperatorNameConventions.SET,
Checks.memberOrExtension, Checks.ValueParametersCount.atLeast(2), Checks.memberOrExtension, Checks.ValueParametersCount.atLeast(2),
Checks.simple("last parameter should not have a default value or be a vararg") { Checks.simple("last parameter should not have a default value or be a vararg") { it, _ ->
it.valueParameters.lastOrNull()?.let { param -> it.valueParameters.lastOrNull()?.let { param ->
param.defaultValue == null && !param.isVararg param.defaultValue == null && !param.isVararg
} == true } == true
@@ -161,9 +162,9 @@ private abstract class Check {
} }
private object Checks { private object Checks {
fun simple(message: String, predicate: (FirSimpleFunction) -> Boolean) = object : Check() { fun simple(message: String, predicate: (FirSimpleFunction, FirSession) -> Boolean) = object : Check() {
override fun check(function: FirSimpleFunction, session: FirSession, scopeSession: ScopeSession?): String? = override fun check(function: FirSimpleFunction, session: FirSession, scopeSession: ScopeSession?): String? =
message.takeIf { !predicate(function) } message.takeIf { !predicate(function, session) }
} }
fun full(message: String, predicate: (FirSession, FirSimpleFunction) -> Boolean) = object : Check() { fun full(message: String, predicate: (FirSession, FirSimpleFunction) -> Boolean) = object : Check() {
@@ -171,51 +172,51 @@ private object Checks {
message.takeIf { !predicate(session, function) } message.takeIf { !predicate(session, function) }
} }
val memberOrExtension = simple("must be a member or an extension function") { val memberOrExtension = simple("must be a member or an extension function") { it, _ ->
it.dispatchReceiverType != null || it.receiverParameter != null it.dispatchReceiverType != null || it.receiverParameter != null
} }
val member = simple("must be a member function") { val member = simple("must be a member function") { it, _ ->
it.dispatchReceiverType != null it.dispatchReceiverType != null
} }
val nonSuspend = simple("must not be suspend") { val nonSuspend = simple("must not be suspend") { it, _ ->
!it.isSuspend !it.isSuspend
} }
object ValueParametersCount { object ValueParametersCount {
fun atLeast(n: Int) = simple("must have at least $n value parameter" + (if (n > 1) "s" else "")) { fun atLeast(n: Int) = simple("must have at least $n value parameter" + (if (n > 1) "s" else "")) { it, _ ->
it.valueParameters.size >= n it.valueParameters.size >= n
} }
fun exactly(n: Int) = simple("must have exactly $n value parameters") { fun exactly(n: Int) = simple("must have exactly $n value parameters") { it, _ ->
it.valueParameters.size == n it.valueParameters.size == n
} }
val single = simple("must have a single value parameter") { val single = simple("must have a single value parameter") { it, _ ->
it.valueParameters.size == 1 it.valueParameters.size == 1
} }
val none = simple("must have no value parameters") { val none = simple("must have no value parameters") { it, _ ->
it.valueParameters.isEmpty() it.valueParameters.isEmpty()
} }
} }
object Returns { object Returns {
val boolean = simple("must return Boolean") { val boolean = simple("must return Boolean") { it, session ->
it.returnTypeRef.isBoolean it.returnTypeRef.coneType.fullyExpandedType(session).isBoolean
} }
val int = simple("must return Int") { val int = simple("must return Int") { it, session ->
it.returnTypeRef.isInt it.returnTypeRef.coneType.fullyExpandedType(session).isInt
} }
val unit = simple("must return Unit") { val unit = simple("must return Unit") { it, session ->
it.returnTypeRef.isUnit it.returnTypeRef.coneType.fullyExpandedType(session).isUnit
} }
} }
val noDefaultAndVarargs = simple("should not have varargs or parameters with default values") { val noDefaultAndVarargs = simple("should not have varargs or parameters with default values") { it, _ ->
it.valueParameters.all { param -> it.valueParameters.all { param ->
param.defaultValue == null && !param.isVararg param.defaultValue == null && !param.isVararg
} }
@@ -88,7 +88,10 @@ fun FirSimpleFunction.isTypedEqualsInValueClass(session: FirSession): Boolean =
contextReceivers.isEmpty() && receiverParameter == null contextReceivers.isEmpty() && receiverParameter == null
&& name == OperatorNameConventions.EQUALS && name == OperatorNameConventions.EQUALS
&& this@run.isInline && valueParameters.size == 1 && this@run.isInline && valueParameters.size == 1
&& (returnTypeRef.isBoolean || returnTypeRef.isNothing) && returnTypeRef.coneType.fullyExpandedType(session).let {
&& valueParameters[0].returnTypeRef.coneType.let { it is ConeClassLikeType && it.replaceArgumentsWithStarProjections() == valueClassStarProjection } it.isBoolean || it.isNothing
} && valueParameters[0].returnTypeRef.coneType.let {
it is ConeClassLikeType && it.replaceArgumentsWithStarProjections() == valueClassStarProjection
}
} }
} ?: false } == true
@@ -17,7 +17,9 @@ import org.jetbrains.kotlin.fir.symbols.impl.*
import org.jetbrains.kotlin.fir.types.ConeClassLikeType import org.jetbrains.kotlin.fir.types.ConeClassLikeType
import org.jetbrains.kotlin.fir.types.coneType import org.jetbrains.kotlin.fir.types.coneType
import org.jetbrains.kotlin.fir.types.coneTypeSafe import org.jetbrains.kotlin.fir.types.coneTypeSafe
import org.jetbrains.kotlin.fir.types.isNullableAny
import org.jetbrains.kotlin.fir.types.toSymbol import org.jetbrains.kotlin.fir.types.toSymbol
import org.jetbrains.kotlin.util.OperatorNameConventions
fun FirClass.constructors(session: FirSession): List<FirConstructorSymbol> { fun FirClass.constructors(session: FirSession): List<FirConstructorSymbol> {
val result = mutableListOf<FirConstructorSymbol>() val result = mutableListOf<FirConstructorSymbol>()
@@ -93,3 +95,12 @@ private fun FirFunction.containsDefaultValue(index: Int): Boolean = valueParamet
fun FirFunction.itOrExpectHasDefaultParameterValue(index: Int): Boolean = fun FirFunction.itOrExpectHasDefaultParameterValue(index: Int): Boolean =
containsDefaultValue(index) || symbol.getSingleExpectForActualOrNull()?.fir?.containsDefaultValue(index) == true containsDefaultValue(index) || symbol.getSingleExpectForActualOrNull()?.fir?.containsDefaultValue(index) == true
fun FirSimpleFunction.isEquals(session: FirSession): Boolean {
if (name != OperatorNameConventions.EQUALS) return false
if (valueParameters.size != 1) return false
if (contextReceivers.isNotEmpty()) return false
if (receiverParameter != null) return false
val parameter = valueParameters.first()
return parameter.returnTypeRef.coneType.fullyExpandedType(session).isNullableAny
}
@@ -24,6 +24,7 @@ import org.jetbrains.kotlin.fir.declarations.builder.FirSimpleFunctionBuilder
import org.jetbrains.kotlin.fir.declarations.builder.buildSimpleFunction import org.jetbrains.kotlin.fir.declarations.builder.buildSimpleFunction
import org.jetbrains.kotlin.fir.declarations.builder.buildValueParameter import org.jetbrains.kotlin.fir.declarations.builder.buildValueParameter
import org.jetbrains.kotlin.fir.declarations.impl.FirResolvedDeclarationStatusImpl import org.jetbrains.kotlin.fir.declarations.impl.FirResolvedDeclarationStatusImpl
import org.jetbrains.kotlin.fir.declarations.isEquals
import org.jetbrains.kotlin.fir.resolve.ScopeSession import org.jetbrains.kotlin.fir.resolve.ScopeSession
import org.jetbrains.kotlin.fir.resolve.defaultType import org.jetbrains.kotlin.fir.resolve.defaultType
import org.jetbrains.kotlin.fir.resolve.lookupSuperTypes import org.jetbrains.kotlin.fir.resolve.lookupSuperTypes
@@ -46,7 +47,7 @@ import org.jetbrains.kotlin.utils.addToStdlib.shouldNotBeCalled
* This declared scope wrapper is created for data/value classes and provides Any method stubs, if necessary * This declared scope wrapper is created for data/value classes and provides Any method stubs, if necessary
*/ */
class FirClassAnySynthesizedMemberScope( class FirClassAnySynthesizedMemberScope(
session: FirSession, private val session: FirSession,
private val declaredMemberScope: FirContainingNamesAwareScope, private val declaredMemberScope: FirContainingNamesAwareScope,
klass: FirRegularClass, klass: FirRegularClass,
scopeSession: ScopeSession, scopeSession: ScopeSession,
@@ -118,7 +119,7 @@ class FirClassAnySynthesizedMemberScope(
} }
else -> { else -> {
lazyResolveToPhase(FirResolvePhase.TYPES) lazyResolveToPhase(FirResolvePhase.TYPES)
fir.isEquals() fir.isEquals(session)
} }
} }
} }
@@ -54,16 +54,17 @@ import kotlin.contracts.contract
fun FirAnonymousFunction.shouldReturnUnit(returnStatements: Collection<FirExpression>): Boolean = fun FirAnonymousFunction.shouldReturnUnit(returnStatements: Collection<FirExpression>): Boolean =
isLambda && returnStatements.any { it is FirUnitExpression } isLambda && returnStatements.any { it is FirUnitExpression }
fun FirAnonymousFunction.addReturnToLastStatementIfNeeded() { fun FirAnonymousFunction.addReturnToLastStatementIfNeeded(session: FirSession) {
// If this lambda's resolved, expected return type is Unit, we don't need an explicit return statement. // If this lambda's resolved, expected return type is Unit, we don't need an explicit return statement.
// During conversion (to backend IR), the last expression will be coerced to Unit if needed. // During conversion (to backend IR), the last expression will be coerced to Unit if needed.
if (returnTypeRef.isUnit) return if (returnTypeRef.coneType.fullyExpandedType(session).isUnit) return
val body = this.body ?: return val body = this.body ?: return
val lastStatement = body.statements.lastOrNull() as? FirExpression ?: return val lastStatement = body.statements.lastOrNull() as? FirExpression ?: return
if (lastStatement is FirReturnExpression) return if (lastStatement is FirReturnExpression) return
val returnType = (body.typeRef as? FirResolvedTypeRef) ?: return val returnType = (body.typeRef as? FirResolvedTypeRef) ?: return
@OptIn(UnexpandedTypeCheck::class)
if (returnType.isNothing) return if (returnType.isNothing) return
val returnTarget = FirFunctionTarget(null, isLambda = isLambda).also { it.bind(this) } val returnTarget = FirFunctionTarget(null, isLambda = isLambda).also { it.bind(this) }
@@ -21,6 +21,7 @@ import org.jetbrains.kotlin.fir.resolve.BodyResolveComponents
import org.jetbrains.kotlin.fir.resolve.DoubleColonLHS import org.jetbrains.kotlin.fir.resolve.DoubleColonLHS
import org.jetbrains.kotlin.fir.resolve.createFunctionType import org.jetbrains.kotlin.fir.resolve.createFunctionType
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeUnsupportedCallableReferenceTarget import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeUnsupportedCallableReferenceTarget
import org.jetbrains.kotlin.fir.resolve.fullyExpandedType
import org.jetbrains.kotlin.fir.resolve.inference.extractInputOutputTypesFromCallableReferenceExpectedType import org.jetbrains.kotlin.fir.resolve.inference.extractInputOutputTypesFromCallableReferenceExpectedType
import org.jetbrains.kotlin.fir.resolve.inference.model.ConeArgumentConstraintPosition import org.jetbrains.kotlin.fir.resolve.inference.model.ConeArgumentConstraintPosition
import org.jetbrains.kotlin.fir.resolve.scope import org.jetbrains.kotlin.fir.resolve.scope
@@ -248,10 +249,14 @@ private fun BodyResolveComponents.getCallableReferenceAdaptation(
} }
} }
val coercionStrategy = if (returnExpectedType.isUnitOrFlexibleUnit && !function.returnTypeRef.isUnit) val returnTypeRef = function.returnTypeRef
CoercionStrategy.COERCION_TO_UNIT val coercionStrategy =
else if (returnExpectedType.isUnitOrFlexibleUnit &&
CoercionStrategy.NO_COERCION returnTypeRef.coneTypeSafe<ConeKotlinType>()?.fullyExpandedType(session)?.isUnit != true
)
CoercionStrategy.COERCION_TO_UNIT
else
CoercionStrategy.NO_COERCION
val adaptedArguments = if (expectedType.isBaseTypeForNumberedReferenceTypes) val adaptedArguments = if (expectedType.isBaseTypeForNumberedReferenceTypes)
emptyMap() emptyMap()
@@ -536,7 +536,7 @@ abstract class FirDataFlowAnalyzer(
return session.declaredMemberScope(this, memberRequiredPhase = FirResolvePhase.STATUS) return session.declaredMemberScope(this, memberRequiredPhase = FirResolvePhase.STATUS)
.getFunctions(OperatorNameConventions.EQUALS) .getFunctions(OperatorNameConventions.EQUALS)
.any { it.fir.isEquals() } .any { it.fir.isEquals(session) }
} }
// ----------------------------------- Jump ----------------------------------- // ----------------------------------- Jump -----------------------------------
@@ -1107,6 +1107,7 @@ abstract class FirDataFlowAnalyzer(
} }
} }
@OptIn(UnexpandedTypeCheck::class)
fun exitElvis(elvisExpression: FirElvisExpression, isLhsNotNull: Boolean, callCompleted: Boolean) { fun exitElvis(elvisExpression: FirElvisExpression, isLhsNotNull: Boolean, callCompleted: Boolean) {
val node = graphBuilder.exitElvis(isLhsNotNull, callCompleted) val node = graphBuilder.exitElvis(isLhsNotNull, callCompleted)
node.mergeIncomingFlow { flow -> node.mergeIncomingFlow { flow ->
@@ -1065,6 +1065,7 @@ class ControlFlowGraphBuilder {
// it would be much easier if we could build calls after full completion only, at least for Nothing calls // it would be much easier if we could build calls after full completion only, at least for Nothing calls
// KT-59726 // KT-59726
// @returns `true` if node actually returned Nothing // @returns `true` if node actually returned Nothing
@OptIn(UnexpandedTypeCheck::class)
private fun completeFunctionCall(node: FunctionCallNode): Boolean { private fun completeFunctionCall(node: FunctionCallNode): Boolean {
if (!node.fir.resultType.isNothing) return false if (!node.fir.resultType.isNothing) return false
val stub = StubNode(node.owner, node.level) val stub = StubNode(node.owner, node.level)
@@ -1082,6 +1083,7 @@ class ControlFlowGraphBuilder {
// ----------------------------------- Resolvable call ----------------------------------- // ----------------------------------- Resolvable call -----------------------------------
@OptIn(UnexpandedTypeCheck::class)
fun exitQualifiedAccessExpression(qualifiedAccessExpression: FirQualifiedAccessExpression): QualifiedAccessNode { fun exitQualifiedAccessExpression(qualifiedAccessExpression: FirQualifiedAccessExpression): QualifiedAccessNode {
val returnsNothing = qualifiedAccessExpression.resultType.isNothing val returnsNothing = qualifiedAccessExpression.resultType.isNothing
val node = createQualifiedAccessNode(qualifiedAccessExpression) val node = createQualifiedAccessNode(qualifiedAccessExpression)
@@ -1093,6 +1095,7 @@ class ControlFlowGraphBuilder {
return node return node
} }
@OptIn(UnexpandedTypeCheck::class)
fun exitSmartCastExpression(smartCastExpression: FirSmartCastExpression): SmartCastExpressionExitNode { fun exitSmartCastExpression(smartCastExpression: FirSmartCastExpression): SmartCastExpressionExitNode {
val returnsNothing = smartCastExpression.resultType.isNothing val returnsNothing = smartCastExpression.resultType.isNothing
val node = createSmartCastExitNode(smartCastExpression) val node = createSmartCastExitNode(smartCastExpression)
@@ -1126,6 +1129,7 @@ class ControlFlowGraphBuilder {
return argumentListSplitNodes.pop()?.also { addNewSimpleNode(it) } return argumentListSplitNodes.pop()?.also { addNewSimpleNode(it) }
} }
@OptIn(UnexpandedTypeCheck::class)
fun exitFunctionCall(functionCall: FirFunctionCall, callCompleted: Boolean): FunctionCallNode { fun exitFunctionCall(functionCall: FirFunctionCall, callCompleted: Boolean): FunctionCallNode {
val returnsNothing = functionCall.resultType.isNothing val returnsNothing = functionCall.resultType.isNothing
val node = createFunctionCallNode(functionCall) val node = createFunctionCallNode(functionCall)
@@ -1171,6 +1175,7 @@ class ControlFlowGraphBuilder {
return createThrowExceptionNode(throwExpression).also { addNonSuccessfullyTerminatingNode(it) } return createThrowExceptionNode(throwExpression).also { addNonSuccessfullyTerminatingNode(it) }
} }
@OptIn(UnexpandedTypeCheck::class)
fun exitCheckNotNullCall(checkNotNullCall: FirCheckNotNullCall, callCompleted: Boolean): CheckNotNullCallNode { fun exitCheckNotNullCall(checkNotNullCall: FirCheckNotNullCall, callCompleted: Boolean): CheckNotNullCallNode {
val node = createCheckNotNullCallNode(checkNotNullCall) val node = createCheckNotNullCallNode(checkNotNullCall)
unifyDataFlowFromPostponedLambdas(node, callCompleted) unifyDataFlowFromPostponedLambdas(node, callCompleted)
@@ -249,7 +249,7 @@ class FirCallCompletionResultsWriterTransformer(
} }
if (enableArrayOfCallTransformation) { if (enableArrayOfCallTransformation) {
return arrayOfCallTransformer.transformFunctionCall(result, null) return arrayOfCallTransformer.transformFunctionCall(result, session)
} }
return result return result
@@ -618,7 +618,7 @@ class FirCallCompletionResultsWriterTransformer(
session.lookupTracker?.recordTypeResolveAsLookup(result.typeRef, result.source, context.file.source) session.lookupTracker?.recordTypeResolveAsLookup(result.typeRef, result.source, context.file.source)
} }
// Have to delay this until the type is written to avoid adding a return if the type is Unit. // Have to delay this until the type is written to avoid adding a return if the type is Unit.
result.addReturnToLastStatementIfNeeded() result.addReturnToLastStatementIfNeeded(session)
return result return result
} }
@@ -214,6 +214,7 @@ private object WhenOnNullableExhaustivenessChecker : WhenExhaustivenessChecker()
private object ConditionChecker : AbstractConditionChecker<Flags>() { private object ConditionChecker : AbstractConditionChecker<Flags>() {
override fun visitEqualityOperatorCall(equalityOperatorCall: FirEqualityOperatorCall, data: Flags) { override fun visitEqualityOperatorCall(equalityOperatorCall: FirEqualityOperatorCall, data: Flags) {
val argument = equalityOperatorCall.arguments[1] val argument = equalityOperatorCall.arguments[1]
@OptIn(UnexpandedTypeCheck::class)
if (argument.typeRef.isNullableNothing) { if (argument.typeRef.isNullableNothing) {
data.containsNull = true data.containsNull = true
} }
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.fir.resolve.transformers.body.resolve package org.jetbrains.kotlin.fir.resolve.transformers.body.resolve
import org.jetbrains.kotlin.fir.FirElement import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.FirCallableDeclaration import org.jetbrains.kotlin.fir.declarations.FirCallableDeclaration
import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction
import org.jetbrains.kotlin.fir.expressions.* import org.jetbrains.kotlin.fir.expressions.*
@@ -14,7 +15,10 @@ import org.jetbrains.kotlin.fir.expressions.builder.buildArrayOfCall
import org.jetbrains.kotlin.fir.references.FirResolvedErrorReference import org.jetbrains.kotlin.fir.references.FirResolvedErrorReference
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
import org.jetbrains.kotlin.fir.resolve.calls.FirNamedReferenceWithCandidate import org.jetbrains.kotlin.fir.resolve.calls.FirNamedReferenceWithCandidate
import org.jetbrains.kotlin.fir.resolve.fullyExpandedType
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.fir.types.coneTypeSafe
import org.jetbrains.kotlin.fir.types.isArrayType import org.jetbrains.kotlin.fir.types.isArrayType
import org.jetbrains.kotlin.fir.visitors.FirDefaultTransformer import org.jetbrains.kotlin.fir.visitors.FirDefaultTransformer
@@ -23,9 +27,9 @@ import org.jetbrains.kotlin.fir.visitors.FirDefaultTransformer
* *
* Note that arrayOf() calls only in [FirAnnotation] or the default value of annotation constructor are transformed. * Note that arrayOf() calls only in [FirAnnotation] or the default value of annotation constructor are transformed.
*/ */
class FirArrayOfCallTransformer : FirDefaultTransformer<Nothing?>() { class FirArrayOfCallTransformer : FirDefaultTransformer<FirSession>() {
private fun toArrayOfCall(functionCall: FirFunctionCall): FirArrayOfCall? { private fun toArrayOfCall(functionCall: FirFunctionCall, session: FirSession): FirArrayOfCall? {
if (!functionCall.isArrayOfCall) return null if (!functionCall.isArrayOfCall(session)) return null
if (functionCall.calleeReference !is FirResolvedNamedReference) return null if (functionCall.calleeReference !is FirResolvedNamedReference) return null
return buildArrayOfCall { return buildArrayOfCall {
source = functionCall.source source = functionCall.source
@@ -42,25 +46,25 @@ class FirArrayOfCallTransformer : FirDefaultTransformer<Nothing?>() {
} }
} }
override fun transformFunctionCall(functionCall: FirFunctionCall, data: Nothing?): FirStatement { override fun transformFunctionCall(functionCall: FirFunctionCall, data: FirSession): FirStatement {
functionCall.transformChildren(this, data) functionCall.transformChildren(this, data)
return toArrayOfCall(functionCall) ?: functionCall return toArrayOfCall(functionCall, data) ?: functionCall
} }
override fun <E : FirElement> transformElement(element: E, data: Nothing?): E { override fun <E : FirElement> transformElement(element: E, data: FirSession): E {
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
return (element.transformChildren(this, data) as E) return (element.transformChildren(this, data) as E)
} }
companion object { companion object {
val FirFunctionCall.isArrayOfCall: Boolean fun FirFunctionCall.isArrayOfCall(session: FirSession): Boolean {
get() { val function: FirCallableDeclaration = getOriginalFunction() ?: return false
val function: FirCallableDeclaration = getOriginalFunction() ?: return false val returnTypeRef = function.returnTypeRef
return function is FirSimpleFunction && return function is FirSimpleFunction &&
function.returnTypeRef.isArrayType && returnTypeRef.coneTypeSafe<ConeKotlinType>()?.fullyExpandedType(session)?.isArrayType == true &&
isArrayOf(function, arguments) && isArrayOf(function, arguments) &&
function.receiverParameter == null function.receiverParameter == null
} }
private val arrayOfNames = hashSetOf("kotlin/arrayOf") + private val arrayOfNames = hashSetOf("kotlin/arrayOf") +
hashSetOf( hashSetOf(
@@ -860,7 +860,7 @@ open class FirDeclarationsResolveTransformer(
lambda.replaceTypeRef(lambda.constructFunctionTypeRef(session, resolvedLambdaAtom?.expectedFunctionTypeKind)) lambda.replaceTypeRef(lambda.constructFunctionTypeRef(session, resolvedLambdaAtom?.expectedFunctionTypeKind))
session.lookupTracker?.recordTypeResolveAsLookup(lambda.typeRef, lambda.source, context.file.source) session.lookupTracker?.recordTypeResolveAsLookup(lambda.typeRef, lambda.source, context.file.source)
lambda.addReturnToLastStatementIfNeeded() lambda.addReturnToLastStatementIfNeeded(session)
return lambda return lambda
} }
@@ -451,7 +451,7 @@ open class FirExpressionsResolveTransformer(transformer: FirAbstractBodyResolveT
addReceiversFromExtensions(result) addReceiversFromExtensions(result)
if (enableArrayOfCallTransformation) { if (enableArrayOfCallTransformation) {
return arrayOfCallTransformer.transformFunctionCall(result, null) return arrayOfCallTransformer.transformFunctionCall(result, session)
} }
return result return result
} }
@@ -16,6 +16,7 @@ import org.jetbrains.kotlin.fir.resolve.dfa.PersistentFlow
import org.jetbrains.kotlin.fir.resolve.dfa.controlFlowGraph import org.jetbrains.kotlin.fir.resolve.dfa.controlFlowGraph
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
import org.jetbrains.kotlin.fir.types.FirTypeRef import org.jetbrains.kotlin.fir.types.FirTypeRef
import org.jetbrains.kotlin.fir.types.UnexpandedTypeCheck
import org.jetbrains.kotlin.fir.types.coneType import org.jetbrains.kotlin.fir.types.coneType
import org.jetbrains.kotlin.fir.types.impl.FirImplicitNothingTypeRef import org.jetbrains.kotlin.fir.types.impl.FirImplicitNothingTypeRef
import org.jetbrains.kotlin.fir.types.isNothing import org.jetbrains.kotlin.fir.types.isNothing
@@ -815,6 +816,8 @@ object FirStub : FirExpression() {
override fun <D> transformAnnotations(transformer: FirTransformer<D>, data: D): FirExpression = this override fun <D> transformAnnotations(transformer: FirTransformer<D>, data: D): FirExpression = this
override fun <D> transformChildren(transformer: FirTransformer<D>, data: D): FirElement = this override fun <D> transformChildren(transformer: FirTransformer<D>, data: D): FirElement = this
override fun replaceAnnotations(newAnnotations: List<FirAnnotation>) { assert(newAnnotations.isEmpty()) } override fun replaceAnnotations(newAnnotations: List<FirAnnotation>) { assert(newAnnotations.isEmpty()) }
@OptIn(UnexpandedTypeCheck::class)
override fun replaceTypeRef(newTypeRef: FirTypeRef) { assert(newTypeRef.isNothing) } override fun replaceTypeRef(newTypeRef: FirTypeRef) { assert(newTypeRef.isNothing) }
} }
@@ -96,12 +96,3 @@ val FirCallableSymbol<*>.isExtension: Boolean
is FirProperty -> fir.receiverParameter != null is FirProperty -> fir.receiverParameter != null
is FirVariable -> false is FirVariable -> false
} }
fun FirSimpleFunction.isEquals(): Boolean {
if (name != OperatorNameConventions.EQUALS) return false
if (valueParameters.size != 1) return false
if (contextReceivers.isNotEmpty()) return false
if (receiverParameter != null) return false
val parameter = valueParameters.first()
return parameter.returnTypeRef.isNullableAny
}
@@ -34,15 +34,40 @@ val FirTypeRef.coneType: ConeKotlinType
val FirTypeRef.coneTypeOrNull: ConeKotlinType? val FirTypeRef.coneTypeOrNull: ConeKotlinType?
get() = coneTypeSafe() get() = coneTypeSafe()
@RequiresOptIn(
"This type check never expands type aliases. Use with care (probably Ok for expression & constructor types). " +
"Generally this.coneType.fullyExpandedType(session).isSomeType is better"
)
annotation class UnexpandedTypeCheck
@UnexpandedTypeCheck
val FirTypeRef.isAny: Boolean get() = isBuiltinType(StandardClassIds.Any, false) val FirTypeRef.isAny: Boolean get() = isBuiltinType(StandardClassIds.Any, false)
@UnexpandedTypeCheck
val FirTypeRef.isNullableAny: Boolean get() = isBuiltinType(StandardClassIds.Any, true) val FirTypeRef.isNullableAny: Boolean get() = isBuiltinType(StandardClassIds.Any, true)
@UnexpandedTypeCheck
val FirTypeRef.isNothing: Boolean get() = isBuiltinType(StandardClassIds.Nothing, false) val FirTypeRef.isNothing: Boolean get() = isBuiltinType(StandardClassIds.Nothing, false)
@UnexpandedTypeCheck
val FirTypeRef.isNullableNothing: Boolean get() = isBuiltinType(StandardClassIds.Nothing, true) val FirTypeRef.isNullableNothing: Boolean get() = isBuiltinType(StandardClassIds.Nothing, true)
@UnexpandedTypeCheck
val FirTypeRef.isUnit: Boolean get() = isBuiltinType(StandardClassIds.Unit, false) val FirTypeRef.isUnit: Boolean get() = isBuiltinType(StandardClassIds.Unit, false)
@UnexpandedTypeCheck
val FirTypeRef.isBoolean: Boolean get() = isBuiltinType(StandardClassIds.Boolean, false) val FirTypeRef.isBoolean: Boolean get() = isBuiltinType(StandardClassIds.Boolean, false)
@UnexpandedTypeCheck
val FirTypeRef.isInt: Boolean get() = isBuiltinType(StandardClassIds.Int, false) val FirTypeRef.isInt: Boolean get() = isBuiltinType(StandardClassIds.Int, false)
@UnexpandedTypeCheck
val FirTypeRef.isString: Boolean get() = isBuiltinType(StandardClassIds.String, false) val FirTypeRef.isString: Boolean get() = isBuiltinType(StandardClassIds.String, false)
@UnexpandedTypeCheck
val FirTypeRef.isEnum: Boolean get() = isBuiltinType(StandardClassIds.Enum, false) val FirTypeRef.isEnum: Boolean get() = isBuiltinType(StandardClassIds.Enum, false)
@UnexpandedTypeCheck
val FirTypeRef.isArrayType: Boolean val FirTypeRef.isArrayType: Boolean
get() = get() =
isBuiltinType(StandardClassIds.Array, false) isBuiltinType(StandardClassIds.Array, false)