org.jetbrains.kotlin.resolve.checkers.PassingProgressionAsCollectionCallChecker.kt Maven / Gradle / Ivy
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.resolve.checkers
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.diagnostics.Errors.PROGRESSIONS_CHANGING_RESOLVE
import org.jetbrains.kotlin.resolve.calls.KotlinCallResolver
import org.jetbrains.kotlin.resolve.calls.checkers.CallCheckerWithAdditionalResolve
import org.jetbrains.kotlin.resolve.calls.components.KotlinResolutionCallbacks
import org.jetbrains.kotlin.resolve.calls.context.BasicCallResolutionContext
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.resolve.calls.model.SimpleKotlinCallArgument
import org.jetbrains.kotlin.resolve.calls.results.OverloadResolutionResults
import org.jetbrains.kotlin.resolve.calls.tower.*
import org.jetbrains.kotlin.resolve.calls.util.replaceArguments
import org.jetbrains.kotlin.resolve.calls.util.replaceTypes
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
import org.jetbrains.kotlin.types.*
import org.jetbrains.kotlin.types.checker.ClassicTypeCheckerState
import org.jetbrains.kotlin.types.checker.ClassicTypeCheckerStateInternals
import org.jetbrains.kotlin.types.checker.intersectTypes
import org.jetbrains.kotlin.types.typeUtil.isTypeParameter
/*
NB: this checker is exceptionally temporary added for stdlib migration purposes (see KT-49276).
Please don't use similar logic for other checkers
*/
@OptIn(ClassicTypeCheckerStateInternals::class)
class PassingProgressionAsCollectionCallChecker(private val kotlinCallResolver: KotlinCallResolver) : CallCheckerWithAdditionalResolve {
private val typeCheckerState = ClassicTypeCheckerState(isErrorTypeEqualsToAnything = false)
private val iterableProgressions = listOf(
CHAR_RANGE_FQN, CHAR_PROGRESSION_FQN,
INT_RANGE_FQN, INT_PROGRESSION_FQN,
LONG_RANGE_FQN, LONG_PROGRESSION_FQN,
UINT_RANGE_FQN, UINT_PROGRESSION_FQN,
ULONG_RANGE_FQN, ULONG_PROGRESSION_FQN
)
private fun check(
resolvedCall: ResolvedCall<*>,
scopeTower: ImplicitScopeTower,
resolutionCallbacks: KotlinResolutionCallbacks,
expectedType: UnwrappedType?,
context: BasicCallResolutionContext,
) {
// The stdlib migration is going to be finished in 1.8, checks aren't needed there (DisableCheckingChangedProgressionsResolve has 1.8 since version)
val isCheckingDisabled = context.languageVersionSettings.supportsFeature(LanguageFeature.DisableCheckingChangedProgressionsResolve)
if (isCheckingDisabled || resolvedCall !is NewResolvedCallImpl<*>) return
val kotlinCall = resolvedCall.psiKotlinCall
val valueArguments = kotlinCall.argumentsInParenthesis.takeIf { it.isNotEmpty() } ?: return
val progressionOrRangeArgumentTypes = valueArguments.map {
if (it !is SimpleKotlinCallArgument) return@map null
getRangeOrProgressionElementType(it.receiver.receiverValue.type, iterableProgressions)
}
if (progressionOrRangeArgumentTypes.all { it == null }) return
val builtIns = resolvedCall.candidateDescriptor.builtIns
val newArguments = valueArguments.replaceTypes(context, resolutionCallbacks) { i, type ->
val progressionOrRangeElementType = progressionOrRangeArgumentTypes[i] ?: return@replaceTypes null
intersectTypes(
listOf(
KotlinTypeFactory.simpleNotNullType(
TypeAttributes.Empty,
builtIns.collection,
listOf(TypeProjectionImpl(progressionOrRangeElementType))
),
type
)
)
}
val newCall = kotlinCall.replaceArguments(newArguments, kotlinCall.explicitReceiver)
val candidateForCollectionReplacedArgument = kotlinCallResolver.resolveCall(
scopeTower, resolutionCallbacks, newCall, expectedType, context.collectAllCandidates
).singleOrNull() ?: return
// Resolve wasn't changed or inapplicable
if (
candidateForCollectionReplacedArgument.descriptor == resolvedCall.candidateDescriptor ||
!candidateForCollectionReplacedArgument.isSuccessful
) return
val collectionOfAnyType = makeCollectionOfAnyType(builtIns)
for ((i, argument) in newCall.argumentsInParenthesis.withIndex()) {
// Skip if the argument wasn't a Range/Progression
if (progressionOrRangeArgumentTypes.getOrNull(i) == null) continue
val resolvedCallForCollectionReplacedArgument = candidateForCollectionReplacedArgument.resolvedCall
val alternativeParameterType =
resolvedCallForCollectionReplacedArgument.argumentToCandidateParameter[argument]?.type?.let { type ->
if (type.isTypeParameter()) {
val typeVariable = resolvedCallForCollectionReplacedArgument.freshVariablesSubstitutor.freshVariables.find {
it.originalTypeParameter == type.constructor.declarationDescriptor
}?.freshTypeConstructor ?: return@let null
resolutionCallbacks.findResultType(candidateForCollectionReplacedArgument.getSystem(), typeVariable)
} else type
} ?: continue
val alternativeParameterTypeConstructor = alternativeParameterType.upperIfFlexible().constructor
if (alternativeParameterTypeConstructor.declarationDescriptor != builtIns.collection && (alternativeParameterTypeConstructor !is IntersectionTypeConstructor || alternativeParameterTypeConstructor.supertypes.none { it.constructor.declarationDescriptor == builtIns.collection })) continue
val argumentExpression = argument.psiExpression ?: continue
val initialArgumentType = resolvedCall.candidateDescriptor.valueParameters.getOrNull(i)?.type?.upperIfFlexible() ?: continue
// Iterable initial type is an exception, considered as similar to Collection passing candidate
if (initialArgumentType.constructor.declarationDescriptor == builtIns.iterable) continue
// The initial type should be wider than Collection
if (
AbstractTypeChecker.isSubtypeOf(typeCheckerState, collectionOfAnyType, initialArgumentType)
|| initialArgumentType.isTypeParameter()
) {
context.trace.report(
PROGRESSIONS_CHANGING_RESOLVE.on(
candidateForCollectionReplacedArgument.callComponents.languageVersionSettings,
argumentExpression,
resolvedCallForCollectionReplacedArgument.candidateDescriptor
)
)
}
}
}
override fun check(
overloadResolutionResults: OverloadResolutionResults<*>,
scopeTower: ImplicitScopeTower,
resolutionCallbacks: KotlinResolutionCallbacks,
expectedType: UnwrappedType?,
context: BasicCallResolutionContext,
) {
if (!overloadResolutionResults.isSingleResult) return
val resolvedCall = overloadResolutionResults.resultingCall as? NewAbstractResolvedCall<*> ?: return
check(resolvedCall, scopeTower, resolutionCallbacks, expectedType, context)
}
private fun makeCollectionOfAnyType(builtIns: KotlinBuiltIns): KotlinType =
KotlinTypeFactory.simpleNotNullType(
TypeAttributes.Empty,
builtIns.collection,
listOf(TypeProjectionImpl(builtIns.nullableAnyType))
)
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy