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
|
||||
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 reference: KtExpression? = when (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
|
||||
abstract fun makeValueArgument(): ValueArgument?
|
||||
abstract val valueExpression: KtExpression?
|
||||
open fun makeCalleeExpressionForSpecialCall(): KtExpression? = null
|
||||
open fun getCalleeExpressionForSpecialCall(): KtExpression? = null
|
||||
|
||||
lateinit var dataFlowValue: DataFlowValue; private set
|
||||
|
||||
@@ -146,8 +146,8 @@ class PatternMatchingTypingVisitor internal constructor(facade: ExpressionTyping
|
||||
)
|
||||
}
|
||||
|
||||
override fun makeCalleeExpressionForSpecialCall(): KtExpression? =
|
||||
KtPsiFactory(variable.project, true).createExpression(variable.name!!)
|
||||
override fun getCalleeExpressionForSpecialCall(): KtExpression? =
|
||||
variable
|
||||
|
||||
override val valueExpression: KtExpression?
|
||||
get() = variable.initializer
|
||||
@@ -208,11 +208,6 @@ class PatternMatchingTypingVisitor internal constructor(facade: ExpressionTyping
|
||||
val contextWithExpectedTypeAndSubjectVariable =
|
||||
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)
|
||||
|
||||
val possibleTypesForSubject =
|
||||
@@ -283,11 +278,8 @@ class PatternMatchingTypingVisitor internal constructor(facade: ExpressionTyping
|
||||
val scopeWithSubjectVariable =
|
||||
ExpressionTypingUtils.newWritableScopeImpl(contextBeforeSubject, LexicalScopeKind.WHEN, components.overloadChecker)
|
||||
|
||||
// Destructuring causes SOE in UAST :(
|
||||
val resolveResult =
|
||||
components.localVariableResolver.process(subjectVariable, contextBeforeSubject, contextBeforeSubject.scope, facade)
|
||||
val typeInfo = resolveResult.first
|
||||
val descriptor = resolveResult.second
|
||||
val (typeInfo, descriptor) =
|
||||
components.localVariableResolver.process(subjectVariable, contextBeforeSubject, contextBeforeSubject.scope, facade)
|
||||
|
||||
scopeWithSubjectVariable.addVariableDescriptor(descriptor)
|
||||
|
||||
@@ -313,7 +305,7 @@ class PatternMatchingTypingVisitor internal constructor(facade: ExpressionTyping
|
||||
val wrappedArgumentExpressions = wrapWhenEntryExpressionsAsSpecialCallArguments(expression)
|
||||
val callForWhen = createCallForSpecialConstruction(
|
||||
expression,
|
||||
subject.makeCalleeExpressionForSpecialCall() ?: expression,
|
||||
subject.getCalleeExpressionForSpecialCall() ?: expression,
|
||||
wrappedArgumentExpressions
|
||||
)
|
||||
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.text.StringUtil
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import junit.framework.TestCase
|
||||
import org.jetbrains.kotlin.analyzer.AnalysisResult
|
||||
@@ -570,19 +571,28 @@ abstract class AbstractDiagnosticsTest : BaseDiagnosticsTest() {
|
||||
if (ktFiles.any { file -> AnalyzingUtils.getSyntaxErrorRanges(file).isNotEmpty() }) return
|
||||
|
||||
val resolvedCallsEntries = bindingContext.getSliceContents(BindingContext.RESOLVED_CALL)
|
||||
val unresolvedCallsOnElements = ArrayList<PsiElement>()
|
||||
|
||||
for ((call, resolvedCall) in resolvedCallsEntries) {
|
||||
val element = call.callElement
|
||||
|
||||
val lineAndColumn = DiagnosticUtils.getLineAndColumnInPsiFile(element.containingFile, element.textRange)
|
||||
|
||||
if (!configuredLanguageVersionSettings.supportsFeature(LanguageFeature.NewInference)) {
|
||||
assertTrue(
|
||||
"Resolved call for '${element.text}'$lineAndColumn is not completed",
|
||||
(resolvedCall as MutableResolvedCall<*>).isCompleted
|
||||
)
|
||||
if (!(resolvedCall as MutableResolvedCall<*>).isCompleted) {
|
||||
unresolvedCallsOnElements.add(element)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
|
||||
@@ -22475,6 +22475,11 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
|
||||
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")
|
||||
public void testReassignmentToWhenSubjectVariable() throws Exception {
|
||||
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");
|
||||
}
|
||||
|
||||
@TestMetadata("nestedWhenWithSubject.kt")
|
||||
public void testNestedWhenWithSubject() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/when/withSubjectVariable/nestedWhenWithSubject.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("reassignmentToWhenSubjectVariable.kt")
|
||||
public void testReassignmentToWhenSubjectVariable() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/when/withSubjectVariable/reassignmentToWhenSubjectVariable.kt");
|
||||
|
||||
Reference in New Issue
Block a user