K2: expand type before isSomeType checks properly
Related to KT-60229
This commit is contained in:
committed by
Space Team
parent
51e8a72f47
commit
00b4ae6ae9
+2
-1
@@ -33,6 +33,7 @@ import org.jetbrains.kotlin.fir.psi
|
||||
import org.jetbrains.kotlin.fir.references.*
|
||||
import org.jetbrains.kotlin.fir.resolve.ScopeSession
|
||||
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.scope
|
||||
import org.jetbrains.kotlin.fir.resolve.toSymbol
|
||||
@@ -261,7 +262,7 @@ internal object FirReferenceResolveHelper {
|
||||
return buildList {
|
||||
scope.processFunctionsByName(OperatorNameConventions.EQUALS) { functionSymbol ->
|
||||
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))
|
||||
}
|
||||
}
|
||||
|
||||
+2
@@ -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.types.isNullableAny
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.typeParameterSymbols
|
||||
import org.jetbrains.kotlin.fir.types.UnexpandedTypeCheck
|
||||
|
||||
/**
|
||||
* [KtFirTypeParameterSymbolBase] provides shared implementations for [KtFirTypeParameterSymbol] and [KtFirPsiJavaTypeParameterSymbol].
|
||||
@@ -29,6 +30,7 @@ internal sealed class KtFirTypeParameterSymbolBase : KtTypeParameterSymbol(), Kt
|
||||
KtFirAnnotationListForDeclaration.create(firSymbol, analysisSession.useSiteSession, token)
|
||||
}
|
||||
|
||||
@OptIn(UnexpandedTypeCheck::class)
|
||||
override val upperBounds: List<KtType> by cached {
|
||||
firSymbol.resolvedBounds.mapNotNull { type ->
|
||||
if (type.isNullableAny) return@mapNotNull null
|
||||
|
||||
-1
@@ -40,7 +40,6 @@ import org.jetbrains.kotlin.fir.types.isArrayType
|
||||
import org.jetbrains.kotlin.fir.types.isString
|
||||
import org.jetbrains.kotlin.fir.visitors.FirVisitorVoid
|
||||
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.modules.Module
|
||||
import org.jetbrains.kotlin.modules.TargetId
|
||||
|
||||
+1
-1
@@ -165,7 +165,7 @@ object FirRepeatableAnnotationChecker : FirBasicDeclarationChecker() {
|
||||
val valueParameterSymbols = containerCtor.valueParameterSymbols
|
||||
val parameterName = StandardClassIds.Annotations.ParameterNames.value
|
||||
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()
|
||||
) {
|
||||
reporter.reportOn(
|
||||
|
||||
+2
-2
@@ -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.declarations.FirRegularClass
|
||||
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.isInterface
|
||||
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.util.OperatorNameConventions.TO_STRING
|
||||
|
||||
@@ -43,7 +43,7 @@ object FirMethodOfAnyImplementedInInterfaceChecker : FirRegularClassChecker(), F
|
||||
(function.name == HASHCODE_NAME || function.name == TO_STRING)
|
||||
) {
|
||||
methodOfAny = true
|
||||
} else if (function.isEquals()) {
|
||||
} else if (function.isEquals(context.session)) {
|
||||
methodOfAny = true
|
||||
}
|
||||
|
||||
|
||||
+4
-5
@@ -24,7 +24,6 @@ import org.jetbrains.kotlin.fir.resolve.defaultType
|
||||
import org.jetbrains.kotlin.fir.resolve.fullyExpandedType
|
||||
import org.jetbrains.kotlin.fir.resolve.lookupSuperTypes
|
||||
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.impl.FirImplicitAnyTypeRef
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
@@ -192,7 +191,7 @@ object FirValueClassDeclarationChecker : FirRegularClassChecker() {
|
||||
)
|
||||
}
|
||||
|
||||
primaryConstructorParameter.returnTypeRef.isInapplicableParameterType() -> {
|
||||
primaryConstructorParameter.returnTypeRef.isInapplicableParameterType(context.session) -> {
|
||||
reporter.reportOn(
|
||||
primaryConstructorParameter.returnTypeRef.source,
|
||||
FirErrors.VALUE_CLASS_HAS_INAPPLICABLE_PARAMETER_TYPE,
|
||||
@@ -227,7 +226,7 @@ object FirValueClassDeclarationChecker : FirRegularClassChecker() {
|
||||
if (it !is FirSimpleFunction) {
|
||||
return@forEach
|
||||
}
|
||||
if (it.isEquals()) equalsFromAnyOverriding = it
|
||||
if (it.isEquals(context.session)) equalsFromAnyOverriding = it
|
||||
if (it.isTypedEqualsInValueClass(context.session)) typedEquals = it
|
||||
}
|
||||
equalsFromAnyOverriding to typedEquals
|
||||
@@ -268,8 +267,8 @@ object FirValueClassDeclarationChecker : FirRegularClassChecker() {
|
||||
return isVararg || !primaryConstructorProperty.isVal || isOpen
|
||||
}
|
||||
|
||||
private fun FirTypeRef.isInapplicableParameterType() =
|
||||
isUnit || isNothing
|
||||
private fun FirTypeRef.isInapplicableParameterType(session: FirSession): Boolean =
|
||||
coneType.fullyExpandedType(session).let { it.isUnit || it.isNothing }
|
||||
|
||||
private fun ConeKotlinType.isGenericArrayOfTypeParameter(): Boolean {
|
||||
if (this.typeArguments.firstOrNull() is ConeStarProjection || !isPotentiallyArray())
|
||||
|
||||
+3
@@ -20,6 +20,7 @@ import org.jetbrains.kotlin.fir.languageVersionSettings
|
||||
import org.jetbrains.kotlin.fir.references.FirErrorNamedReference
|
||||
import org.jetbrains.kotlin.fir.types.ConeKotlinType
|
||||
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.isArrayType
|
||||
|
||||
@@ -50,6 +51,8 @@ object FirNamedVarargChecker : FirCallChecker() {
|
||||
val typeRef = argument.expression.typeRef
|
||||
if (typeRef is FirErrorTypeRef) return
|
||||
if (argument.expression is FirArrayOfCall) return
|
||||
|
||||
@OptIn(UnexpandedTypeCheck::class)
|
||||
if (allowAssignArray && typeRef.isArrayType) return
|
||||
|
||||
if (isAnnotation) {
|
||||
|
||||
+1
@@ -30,6 +30,7 @@ object FirNotNullAssertionChecker : FirCheckNotNullCallChecker() {
|
||||
}
|
||||
// TODO: use of Unit is subject to change.
|
||||
// See BodyResolveComponents.typeForQualifier in ResolveUtils.kt which returns Unit for no value type.
|
||||
@OptIn(UnexpandedTypeCheck::class)
|
||||
if (argument is FirResolvedQualifier && argument.typeRef.isUnit) {
|
||||
// Would be reported as NO_COMPANION_OBJECT
|
||||
return
|
||||
|
||||
+5
-1
@@ -17,6 +17,7 @@ import org.jetbrains.kotlin.fir.expressions.FirResolvedQualifier
|
||||
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirTypeAliasSymbol
|
||||
import org.jetbrains.kotlin.fir.types.UnexpandedTypeCheck
|
||||
import org.jetbrains.kotlin.fir.types.isUnit
|
||||
|
||||
object FirStandaloneQualifierChecker : FirResolvedQualifierChecker() {
|
||||
@@ -26,8 +27,11 @@ object FirStandaloneQualifierChecker : FirResolvedQualifierChecker() {
|
||||
if (lastQualifiedAccess?.explicitReceiver === expression || lastQualifiedAccess?.dispatchReceiver === expression) return
|
||||
val lastGetClass = context.getClassCalls.lastOrNull()
|
||||
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
|
||||
|
||||
expression.symbol.reportErrorOn(expression.source, context, reporter)
|
||||
}
|
||||
|
||||
|
||||
+3
-1
@@ -13,6 +13,8 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
|
||||
import org.jetbrains.kotlin.diagnostics.reportOn
|
||||
import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction
|
||||
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
|
||||
|
||||
object RedundantReturnUnitType : FirSimpleFunctionChecker() {
|
||||
@@ -23,7 +25,7 @@ object RedundantReturnUnitType : FirSimpleFunctionChecker() {
|
||||
if (declaration.source?.kind is KtFakeSourceElementKind) 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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,7 +45,8 @@ val ConeKotlinType.isPrimitiveNumberOrNullableType: Boolean
|
||||
val ConeKotlinType.isArrayType: Boolean
|
||||
get() {
|
||||
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]
|
||||
|
||||
+3
-1
@@ -16,6 +16,7 @@ import org.jetbrains.kotlin.fir.declarations.FirContractDescriptionOwner
|
||||
import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction
|
||||
import org.jetbrains.kotlin.fir.diagnostics.ConeDiagnostic
|
||||
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.metadata.ProtoBuf
|
||||
import org.jetbrains.kotlin.metadata.deserialization.isInstanceType
|
||||
@@ -37,7 +38,7 @@ class FirContractDeserializer(private val c: FirDeserializationContext) :
|
||||
override fun extractVariable(
|
||||
valueParameterIndex: Int,
|
||||
owner: FirContractDescriptionOwner
|
||||
): KtValueParameterReference<ConeKotlinType, ConeDiagnostic>? {
|
||||
): KtValueParameterReference<ConeKotlinType, ConeDiagnostic>? {
|
||||
val name: String
|
||||
val ownerFunction = owner as FirSimpleFunction
|
||||
val typeRef = if (valueParameterIndex < 0) {
|
||||
@@ -49,6 +50,7 @@ class FirContractDeserializer(private val c: FirDeserializationContext) :
|
||||
parameter.returnTypeRef
|
||||
} ?: return null
|
||||
|
||||
@OptIn(UnexpandedTypeCheck::class)
|
||||
return if (!typeRef.isBoolean)
|
||||
KtValueParameterReference(valueParameterIndex, name)
|
||||
else
|
||||
|
||||
+3
-3
@@ -196,8 +196,8 @@ internal object FirToConstantValueTransformer : FirDefaultVisitor<ConstantValue<
|
||||
functionCall: FirFunctionCall,
|
||||
data: FirToConstantValueTransformerData
|
||||
): ConstantValue<*>? {
|
||||
if (functionCall.isArrayOfCall) {
|
||||
return FirArrayOfCallTransformer().transformFunctionCall(functionCall, null).accept(this, data)
|
||||
if (functionCall.isArrayOfCall(data.session)) {
|
||||
return FirArrayOfCallTransformer().transformFunctionCall(functionCall, data.session).accept(this, data)
|
||||
}
|
||||
return visitQualifiedAccessExpression(functionCall, data)
|
||||
}
|
||||
@@ -289,7 +289,7 @@ internal object FirToConstantValueChecker : FirDefaultVisitor<Boolean, FirSessio
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
|
||||
+1
@@ -364,6 +364,7 @@ internal class ClassMemberGenerator(
|
||||
* constructor() : this(10) // <---- this call, IrDelegatingConstructorCall
|
||||
* }
|
||||
*/
|
||||
@OptIn(UnexpandedTypeCheck::class)
|
||||
if ((constructor.isFromEnumClass || constructor.returnTypeRef.isEnum) && this.isSuper) {
|
||||
IrEnumConstructorCallImpl(
|
||||
startOffset, endOffset,
|
||||
|
||||
+18
-17
@@ -13,6 +13,7 @@ import org.jetbrains.kotlin.fir.declarations.utils.isSuspend
|
||||
import org.jetbrains.kotlin.fir.languageVersionSettings
|
||||
import org.jetbrains.kotlin.fir.resolve.ScopeSession
|
||||
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.scopes.overriddenFunctions
|
||||
import org.jetbrains.kotlin.fir.types.*
|
||||
@@ -45,7 +46,7 @@ object OperatorFunctionChecks {
|
||||
checkFor(
|
||||
OperatorNameConventions.SET,
|
||||
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 ->
|
||||
param.defaultValue == null && !param.isVararg
|
||||
} == true
|
||||
@@ -161,9 +162,9 @@ private abstract class Check {
|
||||
}
|
||||
|
||||
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? =
|
||||
message.takeIf { !predicate(function) }
|
||||
message.takeIf { !predicate(function, session) }
|
||||
}
|
||||
|
||||
fun full(message: String, predicate: (FirSession, FirSimpleFunction) -> Boolean) = object : Check() {
|
||||
@@ -171,51 +172,51 @@ private object Checks {
|
||||
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
|
||||
}
|
||||
|
||||
val member = simple("must be a member function") {
|
||||
val member = simple("must be a member function") { it, _ ->
|
||||
it.dispatchReceiverType != null
|
||||
}
|
||||
|
||||
val nonSuspend = simple("must not be suspend") {
|
||||
val nonSuspend = simple("must not be suspend") { it, _ ->
|
||||
!it.isSuspend
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
val single = simple("must have a single value parameter") {
|
||||
val single = simple("must have a single value parameter") { it, _ ->
|
||||
it.valueParameters.size == 1
|
||||
}
|
||||
|
||||
val none = simple("must have no value parameters") {
|
||||
val none = simple("must have no value parameters") { it, _ ->
|
||||
it.valueParameters.isEmpty()
|
||||
}
|
||||
}
|
||||
|
||||
object Returns {
|
||||
val boolean = simple("must return Boolean") {
|
||||
it.returnTypeRef.isBoolean
|
||||
val boolean = simple("must return Boolean") { it, session ->
|
||||
it.returnTypeRef.coneType.fullyExpandedType(session).isBoolean
|
||||
}
|
||||
|
||||
val int = simple("must return Int") {
|
||||
it.returnTypeRef.isInt
|
||||
val int = simple("must return Int") { it, session ->
|
||||
it.returnTypeRef.coneType.fullyExpandedType(session).isInt
|
||||
}
|
||||
|
||||
val unit = simple("must return Unit") {
|
||||
it.returnTypeRef.isUnit
|
||||
val unit = simple("must return Unit") { it, session ->
|
||||
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 ->
|
||||
param.defaultValue == null && !param.isVararg
|
||||
}
|
||||
|
||||
+6
-3
@@ -88,7 +88,10 @@ fun FirSimpleFunction.isTypedEqualsInValueClass(session: FirSession): Boolean =
|
||||
contextReceivers.isEmpty() && receiverParameter == null
|
||||
&& name == OperatorNameConventions.EQUALS
|
||||
&& this@run.isInline && valueParameters.size == 1
|
||||
&& (returnTypeRef.isBoolean || returnTypeRef.isNothing)
|
||||
&& valueParameters[0].returnTypeRef.coneType.let { it is ConeClassLikeType && it.replaceArgumentsWithStarProjections() == valueClassStarProjection }
|
||||
&& returnTypeRef.coneType.fullyExpandedType(session).let {
|
||||
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.coneType
|
||||
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.util.OperatorNameConventions
|
||||
|
||||
fun FirClass.constructors(session: FirSession): List<FirConstructorSymbol> {
|
||||
val result = mutableListOf<FirConstructorSymbol>()
|
||||
@@ -93,3 +95,12 @@ private fun FirFunction.containsDefaultValue(index: Int): Boolean = valueParamet
|
||||
|
||||
fun FirFunction.itOrExpectHasDefaultParameterValue(index: Int): Boolean =
|
||||
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
|
||||
}
|
||||
|
||||
+3
-2
@@ -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.buildValueParameter
|
||||
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.defaultType
|
||||
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
|
||||
*/
|
||||
class FirClassAnySynthesizedMemberScope(
|
||||
session: FirSession,
|
||||
private val session: FirSession,
|
||||
private val declaredMemberScope: FirContainingNamesAwareScope,
|
||||
klass: FirRegularClass,
|
||||
scopeSession: ScopeSession,
|
||||
@@ -118,7 +119,7 @@ class FirClassAnySynthesizedMemberScope(
|
||||
}
|
||||
else -> {
|
||||
lazyResolveToPhase(FirResolvePhase.TYPES)
|
||||
fir.isEquals()
|
||||
fir.isEquals(session)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,16 +54,17 @@ import kotlin.contracts.contract
|
||||
fun FirAnonymousFunction.shouldReturnUnit(returnStatements: Collection<FirExpression>): Boolean =
|
||||
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.
|
||||
// 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 lastStatement = body.statements.lastOrNull() as? FirExpression ?: return
|
||||
if (lastStatement is FirReturnExpression) return
|
||||
|
||||
val returnType = (body.typeRef as? FirResolvedTypeRef) ?: return
|
||||
@OptIn(UnexpandedTypeCheck::class)
|
||||
if (returnType.isNothing) return
|
||||
|
||||
val returnTarget = FirFunctionTarget(null, isLambda = isLambda).also { it.bind(this) }
|
||||
|
||||
+9
-4
@@ -21,6 +21,7 @@ import org.jetbrains.kotlin.fir.resolve.BodyResolveComponents
|
||||
import org.jetbrains.kotlin.fir.resolve.DoubleColonLHS
|
||||
import org.jetbrains.kotlin.fir.resolve.createFunctionType
|
||||
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.model.ConeArgumentConstraintPosition
|
||||
import org.jetbrains.kotlin.fir.resolve.scope
|
||||
@@ -248,10 +249,14 @@ private fun BodyResolveComponents.getCallableReferenceAdaptation(
|
||||
}
|
||||
}
|
||||
|
||||
val coercionStrategy = if (returnExpectedType.isUnitOrFlexibleUnit && !function.returnTypeRef.isUnit)
|
||||
CoercionStrategy.COERCION_TO_UNIT
|
||||
else
|
||||
CoercionStrategy.NO_COERCION
|
||||
val returnTypeRef = function.returnTypeRef
|
||||
val coercionStrategy =
|
||||
if (returnExpectedType.isUnitOrFlexibleUnit &&
|
||||
returnTypeRef.coneTypeSafe<ConeKotlinType>()?.fullyExpandedType(session)?.isUnit != true
|
||||
)
|
||||
CoercionStrategy.COERCION_TO_UNIT
|
||||
else
|
||||
CoercionStrategy.NO_COERCION
|
||||
|
||||
val adaptedArguments = if (expectedType.isBaseTypeForNumberedReferenceTypes)
|
||||
emptyMap()
|
||||
|
||||
+2
-1
@@ -536,7 +536,7 @@ abstract class FirDataFlowAnalyzer(
|
||||
|
||||
return session.declaredMemberScope(this, memberRequiredPhase = FirResolvePhase.STATUS)
|
||||
.getFunctions(OperatorNameConventions.EQUALS)
|
||||
.any { it.fir.isEquals() }
|
||||
.any { it.fir.isEquals(session) }
|
||||
}
|
||||
|
||||
// ----------------------------------- Jump -----------------------------------
|
||||
@@ -1107,6 +1107,7 @@ abstract class FirDataFlowAnalyzer(
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(UnexpandedTypeCheck::class)
|
||||
fun exitElvis(elvisExpression: FirElvisExpression, isLhsNotNull: Boolean, callCompleted: Boolean) {
|
||||
val node = graphBuilder.exitElvis(isLhsNotNull, callCompleted)
|
||||
node.mergeIncomingFlow { flow ->
|
||||
|
||||
+5
@@ -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
|
||||
// KT-59726
|
||||
// @returns `true` if node actually returned Nothing
|
||||
@OptIn(UnexpandedTypeCheck::class)
|
||||
private fun completeFunctionCall(node: FunctionCallNode): Boolean {
|
||||
if (!node.fir.resultType.isNothing) return false
|
||||
val stub = StubNode(node.owner, node.level)
|
||||
@@ -1082,6 +1083,7 @@ class ControlFlowGraphBuilder {
|
||||
|
||||
// ----------------------------------- Resolvable call -----------------------------------
|
||||
|
||||
@OptIn(UnexpandedTypeCheck::class)
|
||||
fun exitQualifiedAccessExpression(qualifiedAccessExpression: FirQualifiedAccessExpression): QualifiedAccessNode {
|
||||
val returnsNothing = qualifiedAccessExpression.resultType.isNothing
|
||||
val node = createQualifiedAccessNode(qualifiedAccessExpression)
|
||||
@@ -1093,6 +1095,7 @@ class ControlFlowGraphBuilder {
|
||||
return node
|
||||
}
|
||||
|
||||
@OptIn(UnexpandedTypeCheck::class)
|
||||
fun exitSmartCastExpression(smartCastExpression: FirSmartCastExpression): SmartCastExpressionExitNode {
|
||||
val returnsNothing = smartCastExpression.resultType.isNothing
|
||||
val node = createSmartCastExitNode(smartCastExpression)
|
||||
@@ -1126,6 +1129,7 @@ class ControlFlowGraphBuilder {
|
||||
return argumentListSplitNodes.pop()?.also { addNewSimpleNode(it) }
|
||||
}
|
||||
|
||||
@OptIn(UnexpandedTypeCheck::class)
|
||||
fun exitFunctionCall(functionCall: FirFunctionCall, callCompleted: Boolean): FunctionCallNode {
|
||||
val returnsNothing = functionCall.resultType.isNothing
|
||||
val node = createFunctionCallNode(functionCall)
|
||||
@@ -1171,6 +1175,7 @@ class ControlFlowGraphBuilder {
|
||||
return createThrowExceptionNode(throwExpression).also { addNonSuccessfullyTerminatingNode(it) }
|
||||
}
|
||||
|
||||
@OptIn(UnexpandedTypeCheck::class)
|
||||
fun exitCheckNotNullCall(checkNotNullCall: FirCheckNotNullCall, callCompleted: Boolean): CheckNotNullCallNode {
|
||||
val node = createCheckNotNullCallNode(checkNotNullCall)
|
||||
unifyDataFlowFromPostponedLambdas(node, callCompleted)
|
||||
|
||||
+2
-2
@@ -249,7 +249,7 @@ class FirCallCompletionResultsWriterTransformer(
|
||||
}
|
||||
|
||||
if (enableArrayOfCallTransformation) {
|
||||
return arrayOfCallTransformer.transformFunctionCall(result, null)
|
||||
return arrayOfCallTransformer.transformFunctionCall(result, session)
|
||||
}
|
||||
|
||||
return result
|
||||
@@ -618,7 +618,7 @@ class FirCallCompletionResultsWriterTransformer(
|
||||
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.
|
||||
result.addReturnToLastStatementIfNeeded()
|
||||
result.addReturnToLastStatementIfNeeded(session)
|
||||
return result
|
||||
}
|
||||
|
||||
|
||||
+1
@@ -214,6 +214,7 @@ private object WhenOnNullableExhaustivenessChecker : WhenExhaustivenessChecker()
|
||||
private object ConditionChecker : AbstractConditionChecker<Flags>() {
|
||||
override fun visitEqualityOperatorCall(equalityOperatorCall: FirEqualityOperatorCall, data: Flags) {
|
||||
val argument = equalityOperatorCall.arguments[1]
|
||||
@OptIn(UnexpandedTypeCheck::class)
|
||||
if (argument.typeRef.isNullableNothing) {
|
||||
data.containsNull = true
|
||||
}
|
||||
|
||||
+18
-14
@@ -6,6 +6,7 @@
|
||||
package org.jetbrains.kotlin.fir.resolve.transformers.body.resolve
|
||||
|
||||
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.FirSimpleFunction
|
||||
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.FirResolvedNamedReference
|
||||
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.types.ConeKotlinType
|
||||
import org.jetbrains.kotlin.fir.types.coneTypeSafe
|
||||
import org.jetbrains.kotlin.fir.types.isArrayType
|
||||
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.
|
||||
*/
|
||||
class FirArrayOfCallTransformer : FirDefaultTransformer<Nothing?>() {
|
||||
private fun toArrayOfCall(functionCall: FirFunctionCall): FirArrayOfCall? {
|
||||
if (!functionCall.isArrayOfCall) return null
|
||||
class FirArrayOfCallTransformer : FirDefaultTransformer<FirSession>() {
|
||||
private fun toArrayOfCall(functionCall: FirFunctionCall, session: FirSession): FirArrayOfCall? {
|
||||
if (!functionCall.isArrayOfCall(session)) return null
|
||||
if (functionCall.calleeReference !is FirResolvedNamedReference) return null
|
||||
return buildArrayOfCall {
|
||||
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)
|
||||
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")
|
||||
return (element.transformChildren(this, data) as E)
|
||||
}
|
||||
|
||||
companion object {
|
||||
val FirFunctionCall.isArrayOfCall: Boolean
|
||||
get() {
|
||||
val function: FirCallableDeclaration = getOriginalFunction() ?: return false
|
||||
return function is FirSimpleFunction &&
|
||||
function.returnTypeRef.isArrayType &&
|
||||
isArrayOf(function, arguments) &&
|
||||
function.receiverParameter == null
|
||||
}
|
||||
fun FirFunctionCall.isArrayOfCall(session: FirSession): Boolean {
|
||||
val function: FirCallableDeclaration = getOriginalFunction() ?: return false
|
||||
val returnTypeRef = function.returnTypeRef
|
||||
return function is FirSimpleFunction &&
|
||||
returnTypeRef.coneTypeSafe<ConeKotlinType>()?.fullyExpandedType(session)?.isArrayType == true &&
|
||||
isArrayOf(function, arguments) &&
|
||||
function.receiverParameter == null
|
||||
}
|
||||
|
||||
private val arrayOfNames = hashSetOf("kotlin/arrayOf") +
|
||||
hashSetOf(
|
||||
|
||||
+1
-1
@@ -860,7 +860,7 @@ open class FirDeclarationsResolveTransformer(
|
||||
|
||||
lambda.replaceTypeRef(lambda.constructFunctionTypeRef(session, resolvedLambdaAtom?.expectedFunctionTypeKind))
|
||||
session.lookupTracker?.recordTypeResolveAsLookup(lambda.typeRef, lambda.source, context.file.source)
|
||||
lambda.addReturnToLastStatementIfNeeded()
|
||||
lambda.addReturnToLastStatementIfNeeded(session)
|
||||
return lambda
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -451,7 +451,7 @@ open class FirExpressionsResolveTransformer(transformer: FirAbstractBodyResolveT
|
||||
addReceiversFromExtensions(result)
|
||||
|
||||
if (enableArrayOfCallTransformation) {
|
||||
return arrayOfCallTransformer.transformFunctionCall(result, null)
|
||||
return arrayOfCallTransformer.transformFunctionCall(result, session)
|
||||
}
|
||||
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.symbols.impl.FirPropertySymbol
|
||||
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.impl.FirImplicitNothingTypeRef
|
||||
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> transformChildren(transformer: FirTransformer<D>, data: D): FirElement = this
|
||||
override fun replaceAnnotations(newAnnotations: List<FirAnnotation>) { assert(newAnnotations.isEmpty()) }
|
||||
|
||||
@OptIn(UnexpandedTypeCheck::class)
|
||||
override fun replaceTypeRef(newTypeRef: FirTypeRef) { assert(newTypeRef.isNothing) }
|
||||
}
|
||||
|
||||
|
||||
@@ -96,12 +96,3 @@ val FirCallableSymbol<*>.isExtension: Boolean
|
||||
is FirProperty -> fir.receiverParameter != null
|
||||
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?
|
||||
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)
|
||||
|
||||
@UnexpandedTypeCheck
|
||||
val FirTypeRef.isNullableAny: Boolean get() = isBuiltinType(StandardClassIds.Any, true)
|
||||
|
||||
@UnexpandedTypeCheck
|
||||
val FirTypeRef.isNothing: Boolean get() = isBuiltinType(StandardClassIds.Nothing, false)
|
||||
|
||||
@UnexpandedTypeCheck
|
||||
val FirTypeRef.isNullableNothing: Boolean get() = isBuiltinType(StandardClassIds.Nothing, true)
|
||||
|
||||
@UnexpandedTypeCheck
|
||||
val FirTypeRef.isUnit: Boolean get() = isBuiltinType(StandardClassIds.Unit, false)
|
||||
|
||||
@UnexpandedTypeCheck
|
||||
val FirTypeRef.isBoolean: Boolean get() = isBuiltinType(StandardClassIds.Boolean, false)
|
||||
|
||||
@UnexpandedTypeCheck
|
||||
val FirTypeRef.isInt: Boolean get() = isBuiltinType(StandardClassIds.Int, false)
|
||||
|
||||
@UnexpandedTypeCheck
|
||||
val FirTypeRef.isString: Boolean get() = isBuiltinType(StandardClassIds.String, false)
|
||||
|
||||
@UnexpandedTypeCheck
|
||||
val FirTypeRef.isEnum: Boolean get() = isBuiltinType(StandardClassIds.Enum, false)
|
||||
|
||||
@UnexpandedTypeCheck
|
||||
val FirTypeRef.isArrayType: Boolean
|
||||
get() =
|
||||
isBuiltinType(StandardClassIds.Array, false)
|
||||
|
||||
Reference in New Issue
Block a user