[NI] Postprocess lambda result expressions on call completion

Should update type to denotable, e.g.,
IntegerValueType(x) to proper numeric type.

TODO extract common code into some ValueArgumentPostprocessor
This commit is contained in:
Dmitry Petrov
2017-04-18 18:26:59 +03:00
committed by Stanislav Erokhin
parent 1d6ed4ef8e
commit 2bdeef7970
6 changed files with 44 additions and 16 deletions
@@ -29,6 +29,7 @@ import org.jetbrains.kotlin.name.Name;
import org.jetbrains.kotlin.name.SpecialNames;
import org.jetbrains.kotlin.psi.*;
import org.jetbrains.kotlin.resolve.BindingTrace;
import org.jetbrains.kotlin.resolve.StatementFilter;
import org.jetbrains.kotlin.resolve.TemporaryBindingTrace;
import org.jetbrains.kotlin.resolve.TypeResolver;
import org.jetbrains.kotlin.resolve.calls.callResolverUtil.ResolveArgumentsMode;
@@ -398,12 +399,22 @@ public class ArgumentTypeResolver {
@NotNull ResolutionContext context,
@NotNull KtExpression expression
) {
KotlinType type = context.trace.getType(expression);
return updateResultArgumentTypeIfNotDenotable(context.trace, context.statementFilter, context.expectedType, expression);
}
@Nullable
public KotlinType updateResultArgumentTypeIfNotDenotable(
@NotNull BindingTrace trace,
@NotNull StatementFilter statementFilter,
@NotNull KotlinType expectedType,
@NotNull KtExpression expression
) {
KotlinType type = trace.getType(expression);
if (type != null && !type.getConstructor().isDenotable()) {
if (type.getConstructor() instanceof IntegerValueTypeConstructor) {
IntegerValueTypeConstructor constructor = (IntegerValueTypeConstructor) type.getConstructor();
KotlinType primitiveType = TypeUtils.getPrimitiveNumberType(constructor, context.expectedType);
constantExpressionEvaluator.updateNumberType(primitiveType, expression, context.statementFilter, context.trace);
KotlinType primitiveType = TypeUtils.getPrimitiveNumberType(constructor, expectedType);
constantExpressionEvaluator.updateNumberType(primitiveType, expression, statementFilter, trace);
return primitiveType;
}
}
@@ -27,16 +27,13 @@ import org.jetbrains.kotlin.psi.KtPsiUtil
import org.jetbrains.kotlin.psi.psiUtil.lastBlockStatementOrThis
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.BindingTrace
import org.jetbrains.kotlin.resolve.calls.ArgumentTypeResolver
import org.jetbrains.kotlin.resolve.calls.components.LambdaAnalyzer
import org.jetbrains.kotlin.types.TypeApproximator
import org.jetbrains.kotlin.types.TypeApproximatorConfiguration
import org.jetbrains.kotlin.resolve.calls.context.ContextDependency
import org.jetbrains.kotlin.resolve.calls.model.*
import org.jetbrains.kotlin.resolve.calls.util.CallMaker
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.TypeUtils
import org.jetbrains.kotlin.types.UnwrappedType
import org.jetbrains.kotlin.types.*
import org.jetbrains.kotlin.types.expressions.ExpressionTypingServices
import org.jetbrains.kotlin.types.expressions.KotlinTypeInfo
@@ -44,10 +41,11 @@ class LambdaAnalyzerImpl(
val expressionTypingServices: ExpressionTypingServices,
val trace: BindingTrace,
val typeApproximator: TypeApproximator,
val kotlinToResolvedCallTransformer: KotlinToResolvedCallTransformer
val kotlinToResolvedCallTransformer: KotlinToResolvedCallTransformer,
val argumentTypeResolver: ArgumentTypeResolver
): LambdaAnalyzer {
override fun analyzeAndGetRelatedCalls(
override fun analyzeAndGetLambdaResultArguments(
topLevelCall: KotlinCall,
lambdaArgument: LambdaKotlinCallArgument,
receiverType: UnwrappedType?,
@@ -102,8 +100,21 @@ class LambdaAnalyzerImpl(
is FunctionExpressionImpl -> psiCallArgument.ktFunction
else -> throw AssertionError("Unexpected psiCallArgument for resolved lambda argument: $psiCallArgument")
}
val functionDescriptor = trace.bindingContext.get(BindingContext.FUNCTION, ktFunction) as? FunctionDescriptorImpl ?:
throw AssertionError("No function descriptor for resolved lambda argument")
functionDescriptor.setReturnType(returnType)
for (lambdaResult in lambdaArgument.resultArguments) {
val resultValueArgument = lambdaResult.psiCallArgument.valueArgument
val deparenthesized = resultValueArgument.getArgumentExpression()?.let {
KtPsiUtil.getLastElementDeparenthesized(it, expressionTypingServices.statementFilter)
} ?: continue
val recordedType = trace.getType(deparenthesized)
if (recordedType != null && !recordedType.constructor.isDenotable) {
argumentTypeResolver.updateResultArgumentTypeIfNotDenotable(trace, expressionTypingServices.statementFilter, returnType, deparenthesized)
}
}
}
}
@@ -77,7 +77,8 @@ class PSICallResolver(
val constraintInjector: ConstraintInjector,
private val kotlinToResolvedCallTransformer: KotlinToResolvedCallTransformer,
private val kotlinCallResolver: KotlinCallResolver,
private val typeApproximator: TypeApproximator
private val typeApproximator: TypeApproximator,
private val argumentTypeResolver: ArgumentTypeResolver
) {
private val GIVEN_CANDIDATES_NAME = Name.special("<given candidates>")
@@ -89,7 +90,7 @@ class PSICallResolver(
) : OverloadResolutionResults<D> {
val kotlinCall = toKotlinCall(context, resolutionKind.kotlinCallKind, context.call, name, tracingStrategy)
val scopeTower = ASTScopeTower(context)
val lambdaAnalyzer = LambdaAnalyzerImpl(expressionTypingServices, context.trace, typeApproximator, kotlinToResolvedCallTransformer)
val lambdaAnalyzer = createLambdaAnalyzer(context)
val callContext = createCallContext(scopeTower, lambdaAnalyzer)
val factoryProviderForInvoke = FactoryProviderForInvoke(context, callContext, kotlinCall)
@@ -112,7 +113,7 @@ class PSICallResolver(
val kotlinCall = toKotlinCall(context, KotlinCallKind.FUNCTION, context.call, GIVEN_CANDIDATES_NAME, tracingStrategy, dispatchReceiver)
val scopeTower = ASTScopeTower(context)
val lambdaAnalyzer = LambdaAnalyzerImpl(expressionTypingServices, context.trace, typeApproximator, kotlinToResolvedCallTransformer)
val lambdaAnalyzer = createLambdaAnalyzer(context)
val callContext = createCallContext(scopeTower, lambdaAnalyzer)
val givenCandidates = resolutionCandidates.map {
@@ -126,6 +127,9 @@ class PSICallResolver(
}
private fun createLambdaAnalyzer(context: BasicCallResolutionContext) =
LambdaAnalyzerImpl(expressionTypingServices, context.trace, typeApproximator, kotlinToResolvedCallTransformer, argumentTypeResolver)
private fun calculateExpectedType(context: BasicCallResolutionContext): UnwrappedType? {
val expectedType = context.expectedType.unwrap()
@@ -26,7 +26,7 @@ interface IsDescriptorFromSourcePredicate: (CallableDescriptor) -> Boolean
interface CommonSupertypeCalculator: (Collection<UnwrappedType>) -> UnwrappedType
interface LambdaAnalyzer {
fun analyzeAndGetRelatedCalls(
fun analyzeAndGetLambdaResultArguments(
topLevelCall: KotlinCall,
lambdaArgument: LambdaKotlinCallArgument,
receiverType: UnwrappedType?,
@@ -245,10 +245,10 @@ class KotlinCallCompleter(
val receiver = lambda.receiver?.let(::substitute)
val parameters = lambda.parameters.map(::substitute)
val expectedType = lambda.returnType.takeIf { c.canBeProper(it) }?.let(::substitute)
val callsFromLambda = lambdaAnalyzer.analyzeAndGetRelatedCalls(topLevelCall, lambda.argument, receiver, parameters, expectedType)
lambda.analyzed = true
lambda.resultArguments = lambdaAnalyzer.analyzeAndGetLambdaResultArguments(topLevelCall, lambda.argument, receiver, parameters, expectedType)
for (innerCall in callsFromLambda) {
for (innerCall in lambda.resultArguments) {
// todo strange code -- why top-level kotlinCall? may be it isn't right outer call
CheckArguments.checkArgument(topLevelCallContext, topLevelCall, c.getBuilder(), innerCall, lambda.returnType)
}
@@ -51,6 +51,8 @@ class ResolvedLambdaArgument(
override val inputType: Collection<UnwrappedType> get() = receiver?.let { parameters + it } ?: parameters
override val outputType: UnwrappedType get() = returnType
lateinit var resultArguments: List<KotlinCallArgument>
}