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.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))
}
}
@@ -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
@@ -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
@@ -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(
@@ -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
}
@@ -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())
@@ -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) {
@@ -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
@@ -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)
}
@@ -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]
@@ -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
@@ -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)
}
@@ -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,
@@ -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
}
@@ -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
}
@@ -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) }
@@ -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()
@@ -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 ->
@@ -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)
@@ -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
}
@@ -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
}
@@ -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(
@@ -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
}
@@ -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)