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