From 90927cfd1937cd45b1a3a3683d29ea53feb43136 Mon Sep 17 00:00:00 2001 From: Mikhail Glukhikh Date: Mon, 15 Feb 2016 10:54:25 +0300 Subject: [PATCH] CallExpressionResolver.getQualifiedExpressionTypeInfo refactoring --- .../resolve/calls/CallExpressionResolver.kt | 202 ++++++++++-------- 1 file changed, 107 insertions(+), 95 deletions(-) diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CallExpressionResolver.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CallExpressionResolver.kt index a4f51f5545a..ff5fe6a9b6a 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CallExpressionResolver.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CallExpressionResolver.kt @@ -251,30 +251,6 @@ class CallExpressionResolver( return noTypeInfo(context) } - private fun getSelectorReturnTypeInfo( - receiver: Receiver, - callOperationNode: ASTNode?, - selectorExpression: KtExpression?, - context: ExpressionTypingContext, - initialDataFlowInfoForArguments: DataFlowInfo - ): KotlinTypeInfo { - when (selectorExpression) { - is KtCallExpression -> { - return getCallExpressionTypeInfoWithoutFinalTypeCheck(selectorExpression, receiver, - callOperationNode, context, initialDataFlowInfoForArguments) - } - is KtSimpleNameExpression -> { - return getSimpleNameExpressionTypeInfo( - selectorExpression, receiver, callOperationNode, context, initialDataFlowInfoForArguments) - } - is KtExpression -> { - expressionTypingServices.getTypeInfo(selectorExpression, context) - context.trace.report(ILLEGAL_SELECTOR.on(selectorExpression, selectorExpression.text)) - } - } - return noTypeInfo(context) - } - private fun KtQualifiedExpression.elementChain(context: ExpressionTypingContext) = qualifiedExpressionResolver.resolveQualifierInExpressionAndUnroll(this, context) { nameExpression -> @@ -295,6 +271,89 @@ class CallExpressionResolver( } } + private fun getUnsafeSelectorTypeInfo( + receiver: Receiver, + callOperationNode: ASTNode?, + selectorExpression: KtExpression?, + context: ExpressionTypingContext, + initialDataFlowInfoForArguments: DataFlowInfo + ): KotlinTypeInfo = when (selectorExpression) { + is KtCallExpression -> getCallExpressionTypeInfoWithoutFinalTypeCheck( + selectorExpression, receiver, callOperationNode, context, initialDataFlowInfoForArguments) + is KtSimpleNameExpression -> getSimpleNameExpressionTypeInfo( + selectorExpression, receiver, callOperationNode, context, initialDataFlowInfoForArguments) + is KtExpression -> { + expressionTypingServices.getTypeInfo(selectorExpression, context) + context.trace.report(ILLEGAL_SELECTOR.on(selectorExpression, selectorExpression.text)) + noTypeInfo(context) + } + else /*null*/ -> noTypeInfo(context) + } + + private fun getSafeOrUnsafeSelectorTypeInfo(receiver: Receiver, element: CallExpressionElement, context: ExpressionTypingContext): + KotlinTypeInfo { + var initialDataFlowInfoForArguments = context.dataFlowInfo + val receiverDataFlowValue = (receiver as? ReceiverValue)?.let { DataFlowValueFactory.createDataFlowValue(it, context) } + val receiverCanBeNull = receiverDataFlowValue != null && + initialDataFlowInfoForArguments.getPredictableNullability(receiverDataFlowValue).canBeNull() + if (receiverDataFlowValue != null && element.safe) { + // Additional "receiver != null" information should be applied if we consider a safe call + if (receiverCanBeNull) { + initialDataFlowInfoForArguments = initialDataFlowInfoForArguments.disequate( + receiverDataFlowValue, DataFlowValue.nullValue(builtIns)) + } + else if (receiver is ReceiverValue) { + reportUnnecessarySafeCall(context.trace, receiver.type, element.node, receiver) + } + } + + val selector = element.selector + var selectorTypeInfo = getUnsafeSelectorTypeInfo(receiver, element.node, selector, context, initialDataFlowInfoForArguments) + + if (receiver is QualifierReceiver) { + resolveDeferredReceiverInQualifiedExpression(receiver, selector, context) + } + + val selectorType = selectorTypeInfo.type + if (selectorType != null) { + if (element.safe && receiverCanBeNull) { + selectorTypeInfo = selectorTypeInfo.replaceType(TypeUtils.makeNullable(selectorType)) + } + // TODO : this is suspicious: remove this code? + if (selector != null) { + context.trace.recordType(selector, selectorTypeInfo.type) + } + } + return selectorTypeInfo + } + + private fun checkSelectorTypeInfo(qualified: KtQualifiedExpression, selectorTypeInfo: KotlinTypeInfo, context: ExpressionTypingContext): + KotlinTypeInfo { + checkNestedClassAccess(qualified, context) + val value = constantExpressionEvaluator.evaluateExpression(qualified, context.trace, context.expectedType) + return if (value != null && value.isPure) { + dataFlowAnalyzer.createCompileTimeConstantTypeInfo(value, qualified, context) + } + else { + if (context.contextDependency == INDEPENDENT) { + dataFlowAnalyzer.checkType(selectorTypeInfo.type, qualified, context) + } + selectorTypeInfo + } + } + + private fun recordResultTypeInfo(qualified: KtQualifiedExpression, resultTypeInfo: KotlinTypeInfo, context: ExpressionTypingContext) { + val trace = context.trace + if (trace.get(BindingContext.PROCESSED, qualified) != true) { + // Store type information (to prevent problems in call completer) + trace.record(BindingContext.PROCESSED, qualified) + trace.record(BindingContext.EXPRESSION_TYPE_INFO, qualified, resultTypeInfo) + // save scope before analyze and fix debugger: see CodeFragmentAnalyzer.correctContextForExpression + trace.recordScope(context.scope, qualified) + context.replaceDataFlowInfo(resultTypeInfo.dataFlowInfo).recordDataFlowInfo(qualified) + } + } + /** * Visits a qualified expression like x.y or x?.z controlling data flow information changes. @@ -314,21 +373,23 @@ class CallExpressionResolver( var resultTypeInfo = receiverTypeInfo - var unconditional = true - var unconditionalDataFlowInfo = receiverTypeInfo.dataFlowInfo + var allUnsafe = true + // Branch point: right before first safe call + var branchPointDataFlowInfo = receiverTypeInfo.dataFlowInfo for (element in elementChain) { - val receiverType = receiverTypeInfo.type ?: ErrorUtils.createErrorType("Type for " + expression.text) - val qualifierReceiver = trace.get(BindingContext.QUALIFIER, element.receiver) as QualifierReceiver? + val receiverType = receiverTypeInfo.type ?: ErrorUtils.createErrorType("Type for " + element.receiver.text) - val receiver = qualifierReceiver ?: ExpressionReceiver.create(element.receiver, receiverType, trace.bindingContext) + val receiver = trace.get(BindingContext.QUALIFIER, element.receiver) as QualifierReceiver? + ?: ExpressionReceiver.create(element.receiver, receiverType, trace.bindingContext) - val lastStage = element.qualified === expression + val qualifiedExpression = element.qualified + val lastStage = qualifiedExpression === expression // Drop NO_EXPECTED_TYPE / INDEPENDENT at last stage val contextForSelector = (if (lastStage) context else currentContext).replaceDataFlowInfo( - if (TypeUtils.isNullableType(receiverType) && !element.safe) { - // Call with nullable receiver: take unconditional data flow info - unconditionalDataFlowInfo + if (receiver is ReceiverValue && TypeUtils.isNullableType(receiver.type) && !element.safe) { + // Call with nullable receiver: take data flow info from branch point + branchPointDataFlowInfo } else { // Take data flow info from the current receiver @@ -336,79 +397,30 @@ class CallExpressionResolver( } ) - var initialDataFlowInfoForArguments = contextForSelector.dataFlowInfo - val receiverDataFlowValue = (receiver as? ReceiverValue)?.let { DataFlowValueFactory.createDataFlowValue(it, context) } - if (receiverDataFlowValue != null && element.safe) { - // Additional "receiver != null" information should be applied if we consider a safe call - if (initialDataFlowInfoForArguments.getPredictableNullability(receiverDataFlowValue).canBeNull()) { - initialDataFlowInfoForArguments = initialDataFlowInfoForArguments.disequate( - receiverDataFlowValue, DataFlowValue.nullValue(builtIns)) - } - else { - reportUnnecessarySafeCall(trace, receiverType, element.node, receiver) - } + val selectorTypeInfo = getSafeOrUnsafeSelectorTypeInfo(receiver, element, contextForSelector) + // if we have only dots and not ?. move branch point further + allUnsafe = allUnsafe && !element.safe + if (allUnsafe) { + branchPointDataFlowInfo = selectorTypeInfo.dataFlowInfo } - val selectorExpression = element.selector - var selectorReturnTypeInfo = getSelectorReturnTypeInfo( - receiver, element.node, selectorExpression, contextForSelector, initialDataFlowInfoForArguments) - var selectorReturnType = selectorReturnTypeInfo.type - - if (qualifierReceiver != null) { - resolveDeferredReceiverInQualifiedExpression(qualifierReceiver, element.qualified, contextForSelector) - } - checkNestedClassAccess(element.qualified, contextForSelector) - - val safeCall = element.safe - if (safeCall && selectorReturnType != null && receiverDataFlowValue != null && - contextForSelector.dataFlowInfo.getPredictableNullability(receiverDataFlowValue).canBeNull()) { - selectorReturnType = TypeUtils.makeNullable(selectorReturnType) - selectorReturnTypeInfo = selectorReturnTypeInfo.replaceType(selectorReturnType) - } - - // TODO : this is suspicious: remove this code? - if (selectorExpression != null && selectorReturnType != null) { - trace.recordType(selectorExpression, selectorReturnType) - } - resultTypeInfo = selectorReturnTypeInfo - val value = constantExpressionEvaluator.evaluateExpression(element.qualified, trace, contextForSelector.expectedType) - if (value != null && value.isPure) { - resultTypeInfo = dataFlowAnalyzer.createCompileTimeConstantTypeInfo(value, element.qualified, contextForSelector) - if (lastStage) return resultTypeInfo - } - if (contextForSelector.contextDependency == INDEPENDENT) { - dataFlowAnalyzer.checkType(resultTypeInfo.type, element.qualified, contextForSelector) + resultTypeInfo = checkSelectorTypeInfo(qualifiedExpression, selectorTypeInfo, contextForSelector). + replaceDataFlowInfo(branchPointDataFlowInfo) + if (!lastStage) { + recordResultTypeInfo(qualifiedExpression, resultTypeInfo, contextForSelector) } // For the next stage, if any, current stage selector is the receiver! - receiverTypeInfo = selectorReturnTypeInfo - // if we have only dots and not ?. move unconditional data flow info further - if (safeCall) { - unconditional = false - } - else if (unconditional) { - unconditionalDataFlowInfo = receiverTypeInfo.dataFlowInfo - } - //noinspection ConstantConditions - if (!lastStage && trace.get(BindingContext.PROCESSED, element.qualified) != true) { - // Store type information (to prevent problems in call completer) - trace.record(BindingContext.PROCESSED, element.qualified) - trace.record(BindingContext.EXPRESSION_TYPE_INFO, element.qualified, - resultTypeInfo.replaceDataFlowInfo(unconditionalDataFlowInfo)) - // save scope before analyze and fix debugger: see CodeFragmentAnalyzer.correctContextForExpression - trace.recordScope(contextForSelector.scope, element.qualified) - contextForSelector.replaceDataFlowInfo(unconditionalDataFlowInfo).recordDataFlowInfo(element.qualified) - } + receiverTypeInfo = selectorTypeInfo } - // if we are at last stage, we should just take result type info and set unconditional data flow info - return resultTypeInfo.replaceDataFlowInfo(unconditionalDataFlowInfo) + return resultTypeInfo } private fun resolveDeferredReceiverInQualifiedExpression( qualifierReceiver: QualifierReceiver, - qualifiedExpression: KtQualifiedExpression, + selectorExpression: KtExpression?, context: ExpressionTypingContext ) { - val calleeExpression = KtPsiUtil.deparenthesize(qualifiedExpression.selectorExpression.getCalleeExpressionIfAny()) + val calleeExpression = KtPsiUtil.deparenthesize(selectorExpression.getCalleeExpressionIfAny()) val selectorDescriptor = (calleeExpression as? KtReferenceExpression)?.let { context.trace.get(BindingContext.REFERENCE_TARGET, it) }