Handle nested when with subject properly
Hack: callee expression for when with subject variable is the subject variable declaration. This solves the problem that all sub-calls in the expression are implicitly considered to have a single common lexical scope (and 'when (val x = ...)' introduces a new lexical scope, which contains 'x').
This commit is contained in:
@@ -142,6 +142,13 @@ fun KtElement.getCall(context: BindingContext): Call? {
|
|||||||
// Do not use Call bound to outer call expression (if any) to prevent stack overflow during analysis
|
// Do not use Call bound to outer call expression (if any) to prevent stack overflow during analysis
|
||||||
if (element is KtCallElement && element.calleeExpression == null) return null
|
if (element is KtCallElement && element.calleeExpression == null) return null
|
||||||
|
|
||||||
|
if (element is KtWhenExpression) {
|
||||||
|
val subjectVariable = element.subjectVariable
|
||||||
|
if (subjectVariable != null) {
|
||||||
|
return subjectVariable.getCall(context)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val parent = element.parent
|
val parent = element.parent
|
||||||
val reference: KtExpression? = when (parent) {
|
val reference: KtExpression? = when (parent) {
|
||||||
is KtInstanceExpressionWithLabel -> parent
|
is KtInstanceExpressionWithLabel -> parent
|
||||||
|
|||||||
+6
-14
@@ -95,7 +95,7 @@ class PatternMatchingTypingVisitor internal constructor(facade: ExpressionTyping
|
|||||||
protected abstract fun createDataFlowValue(contextAfterSubject: ExpressionTypingContext, builtIns: KotlinBuiltIns): DataFlowValue
|
protected abstract fun createDataFlowValue(contextAfterSubject: ExpressionTypingContext, builtIns: KotlinBuiltIns): DataFlowValue
|
||||||
abstract fun makeValueArgument(): ValueArgument?
|
abstract fun makeValueArgument(): ValueArgument?
|
||||||
abstract val valueExpression: KtExpression?
|
abstract val valueExpression: KtExpression?
|
||||||
open fun makeCalleeExpressionForSpecialCall(): KtExpression? = null
|
open fun getCalleeExpressionForSpecialCall(): KtExpression? = null
|
||||||
|
|
||||||
lateinit var dataFlowValue: DataFlowValue; private set
|
lateinit var dataFlowValue: DataFlowValue; private set
|
||||||
|
|
||||||
@@ -146,8 +146,8 @@ class PatternMatchingTypingVisitor internal constructor(facade: ExpressionTyping
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun makeCalleeExpressionForSpecialCall(): KtExpression? =
|
override fun getCalleeExpressionForSpecialCall(): KtExpression? =
|
||||||
KtPsiFactory(variable.project, true).createExpression(variable.name!!)
|
variable
|
||||||
|
|
||||||
override val valueExpression: KtExpression?
|
override val valueExpression: KtExpression?
|
||||||
get() = variable.initializer
|
get() = variable.initializer
|
||||||
@@ -208,11 +208,6 @@ class PatternMatchingTypingVisitor internal constructor(facade: ExpressionTyping
|
|||||||
val contextWithExpectedTypeAndSubjectVariable =
|
val contextWithExpectedTypeAndSubjectVariable =
|
||||||
subject.scopeWithSubject?.let { contextWithExpectedType.replaceScope(it) } ?: contextWithExpectedType
|
subject.scopeWithSubject?.let { contextWithExpectedType.replaceScope(it) } ?: contextWithExpectedType
|
||||||
|
|
||||||
// val subjectType = subjectTypeInfo?.type ?: ErrorUtils.createErrorType("Unknown type")
|
|
||||||
// val jumpOutPossibleInSubject: Boolean = subjectTypeInfo?.jumpOutPossible ?: false
|
|
||||||
// val subjectDataFlowValue = subjectExpression?.let {
|
|
||||||
// facade.components.dataFlowValueFactory.createDataFlowValue(it, subjectType, contextAfterSubject)
|
|
||||||
// } ?: DataFlowValue.nullValue(components.builtIns)
|
|
||||||
subject.initDataFlowValue(contextAfterSubject, components.builtIns)
|
subject.initDataFlowValue(contextAfterSubject, components.builtIns)
|
||||||
|
|
||||||
val possibleTypesForSubject =
|
val possibleTypesForSubject =
|
||||||
@@ -283,11 +278,8 @@ class PatternMatchingTypingVisitor internal constructor(facade: ExpressionTyping
|
|||||||
val scopeWithSubjectVariable =
|
val scopeWithSubjectVariable =
|
||||||
ExpressionTypingUtils.newWritableScopeImpl(contextBeforeSubject, LexicalScopeKind.WHEN, components.overloadChecker)
|
ExpressionTypingUtils.newWritableScopeImpl(contextBeforeSubject, LexicalScopeKind.WHEN, components.overloadChecker)
|
||||||
|
|
||||||
// Destructuring causes SOE in UAST :(
|
val (typeInfo, descriptor) =
|
||||||
val resolveResult =
|
components.localVariableResolver.process(subjectVariable, contextBeforeSubject, contextBeforeSubject.scope, facade)
|
||||||
components.localVariableResolver.process(subjectVariable, contextBeforeSubject, contextBeforeSubject.scope, facade)
|
|
||||||
val typeInfo = resolveResult.first
|
|
||||||
val descriptor = resolveResult.second
|
|
||||||
|
|
||||||
scopeWithSubjectVariable.addVariableDescriptor(descriptor)
|
scopeWithSubjectVariable.addVariableDescriptor(descriptor)
|
||||||
|
|
||||||
@@ -313,7 +305,7 @@ class PatternMatchingTypingVisitor internal constructor(facade: ExpressionTyping
|
|||||||
val wrappedArgumentExpressions = wrapWhenEntryExpressionsAsSpecialCallArguments(expression)
|
val wrappedArgumentExpressions = wrapWhenEntryExpressionsAsSpecialCallArguments(expression)
|
||||||
val callForWhen = createCallForSpecialConstruction(
|
val callForWhen = createCallForSpecialConstruction(
|
||||||
expression,
|
expression,
|
||||||
subject.makeCalleeExpressionForSpecialCall() ?: expression,
|
subject.getCalleeExpressionForSpecialCall() ?: expression,
|
||||||
wrappedArgumentExpressions
|
wrappedArgumentExpressions
|
||||||
)
|
)
|
||||||
val dataFlowInfoForArguments = createDataFlowInfoForArgumentsOfWhenCall(
|
val dataFlowInfoForArguments = createDataFlowInfoForArgumentsOfWhenCall(
|
||||||
|
|||||||
+41
@@ -0,0 +1,41 @@
|
|||||||
|
// !LANGUAGE: +VariableDeclarationInWhenSubject
|
||||||
|
// !DIAGNOSTICS: -UNUSED_VARIABLE -UNUSED_PARAMETER -UNUSED_VALUE
|
||||||
|
|
||||||
|
fun foo() {}
|
||||||
|
fun <T> bar(x: T, y: T) {}
|
||||||
|
|
||||||
|
fun test1() {
|
||||||
|
when (1) {
|
||||||
|
1 ->
|
||||||
|
when (val y = 2) {
|
||||||
|
2 -> foo()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun test2() {
|
||||||
|
when (val x = 1) {
|
||||||
|
1 ->
|
||||||
|
when (val y = 2) {
|
||||||
|
2 -> foo()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun test3() {
|
||||||
|
when (val x = 1) {
|
||||||
|
1 ->
|
||||||
|
when (val <!NAME_SHADOWING!>x<!> = 2) {
|
||||||
|
2 -> foo()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun test4() {
|
||||||
|
when (val x = 1) {
|
||||||
|
1 ->
|
||||||
|
when (val y = 2) {
|
||||||
|
2 -> bar(x, y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+8
@@ -0,0 +1,8 @@
|
|||||||
|
package
|
||||||
|
|
||||||
|
public fun </*0*/ T> bar(/*0*/ x: T, /*1*/ y: T): kotlin.Unit
|
||||||
|
public fun foo(): kotlin.Unit
|
||||||
|
public fun test1(): kotlin.Unit
|
||||||
|
public fun test2(): kotlin.Unit
|
||||||
|
public fun test3(): kotlin.Unit
|
||||||
|
public fun test4(): kotlin.Unit
|
||||||
+16
-6
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.checkers
|
|||||||
|
|
||||||
import com.intellij.openapi.util.io.FileUtil
|
import com.intellij.openapi.util.io.FileUtil
|
||||||
import com.intellij.openapi.util.text.StringUtil
|
import com.intellij.openapi.util.text.StringUtil
|
||||||
|
import com.intellij.psi.PsiElement
|
||||||
import com.intellij.psi.search.GlobalSearchScope
|
import com.intellij.psi.search.GlobalSearchScope
|
||||||
import junit.framework.TestCase
|
import junit.framework.TestCase
|
||||||
import org.jetbrains.kotlin.analyzer.AnalysisResult
|
import org.jetbrains.kotlin.analyzer.AnalysisResult
|
||||||
@@ -570,19 +571,28 @@ abstract class AbstractDiagnosticsTest : BaseDiagnosticsTest() {
|
|||||||
if (ktFiles.any { file -> AnalyzingUtils.getSyntaxErrorRanges(file).isNotEmpty() }) return
|
if (ktFiles.any { file -> AnalyzingUtils.getSyntaxErrorRanges(file).isNotEmpty() }) return
|
||||||
|
|
||||||
val resolvedCallsEntries = bindingContext.getSliceContents(BindingContext.RESOLVED_CALL)
|
val resolvedCallsEntries = bindingContext.getSliceContents(BindingContext.RESOLVED_CALL)
|
||||||
|
val unresolvedCallsOnElements = ArrayList<PsiElement>()
|
||||||
|
|
||||||
for ((call, resolvedCall) in resolvedCallsEntries) {
|
for ((call, resolvedCall) in resolvedCallsEntries) {
|
||||||
val element = call.callElement
|
val element = call.callElement
|
||||||
|
|
||||||
val lineAndColumn = DiagnosticUtils.getLineAndColumnInPsiFile(element.containingFile, element.textRange)
|
|
||||||
|
|
||||||
if (!configuredLanguageVersionSettings.supportsFeature(LanguageFeature.NewInference)) {
|
if (!configuredLanguageVersionSettings.supportsFeature(LanguageFeature.NewInference)) {
|
||||||
assertTrue(
|
if (!(resolvedCall as MutableResolvedCall<*>).isCompleted) {
|
||||||
"Resolved call for '${element.text}'$lineAndColumn is not completed",
|
unresolvedCallsOnElements.add(element)
|
||||||
(resolvedCall as MutableResolvedCall<*>).isCompleted
|
}
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (unresolvedCallsOnElements.isNotEmpty()) {
|
||||||
|
TestCase.fail(
|
||||||
|
"There are uncompleted resolved calls for the following elements:\n" +
|
||||||
|
unresolvedCallsOnElements.joinToString(separator = "\n") { element ->
|
||||||
|
val lineAndColumn = DiagnosticUtils.getLineAndColumnInPsiFile(element.containingFile, element.textRange)
|
||||||
|
"'${element.text}'$lineAndColumn"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
checkResolvedCallsInDiagnostics(bindingContext, configuredLanguageVersionSettings)
|
checkResolvedCallsInDiagnostics(bindingContext, configuredLanguageVersionSettings)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22475,6 +22475,11 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
|
|||||||
runTest("compiler/testData/diagnostics/tests/when/withSubjectVariable/invisibleOutsideOfWhen.kt");
|
runTest("compiler/testData/diagnostics/tests/when/withSubjectVariable/invisibleOutsideOfWhen.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("nestedWhenWithSubject.kt")
|
||||||
|
public void testNestedWhenWithSubject() throws Exception {
|
||||||
|
runTest("compiler/testData/diagnostics/tests/when/withSubjectVariable/nestedWhenWithSubject.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("reassignmentToWhenSubjectVariable.kt")
|
@TestMetadata("reassignmentToWhenSubjectVariable.kt")
|
||||||
public void testReassignmentToWhenSubjectVariable() throws Exception {
|
public void testReassignmentToWhenSubjectVariable() throws Exception {
|
||||||
runTest("compiler/testData/diagnostics/tests/when/withSubjectVariable/reassignmentToWhenSubjectVariable.kt");
|
runTest("compiler/testData/diagnostics/tests/when/withSubjectVariable/reassignmentToWhenSubjectVariable.kt");
|
||||||
|
|||||||
Generated
+5
@@ -22475,6 +22475,11 @@ public class DiagnosticsUsingJavacTestGenerated extends AbstractDiagnosticsUsing
|
|||||||
runTest("compiler/testData/diagnostics/tests/when/withSubjectVariable/invisibleOutsideOfWhen.kt");
|
runTest("compiler/testData/diagnostics/tests/when/withSubjectVariable/invisibleOutsideOfWhen.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("nestedWhenWithSubject.kt")
|
||||||
|
public void testNestedWhenWithSubject() throws Exception {
|
||||||
|
runTest("compiler/testData/diagnostics/tests/when/withSubjectVariable/nestedWhenWithSubject.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("reassignmentToWhenSubjectVariable.kt")
|
@TestMetadata("reassignmentToWhenSubjectVariable.kt")
|
||||||
public void testReassignmentToWhenSubjectVariable() throws Exception {
|
public void testReassignmentToWhenSubjectVariable() throws Exception {
|
||||||
runTest("compiler/testData/diagnostics/tests/when/withSubjectVariable/reassignmentToWhenSubjectVariable.kt");
|
runTest("compiler/testData/diagnostics/tests/when/withSubjectVariable/reassignmentToWhenSubjectVariable.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user