Resolve variable declaration in 'when' subject
This commit is contained in:
+49
-5
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.types.expressions
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns.isBoolean
|
||||
import org.jetbrains.kotlin.cfg.WhenChecker
|
||||
import org.jetbrains.kotlin.config.LanguageFeature
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
@@ -34,6 +35,8 @@ import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo
|
||||
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValue
|
||||
import org.jetbrains.kotlin.resolve.calls.util.CallMaker
|
||||
import org.jetbrains.kotlin.resolve.checkers.PrimitiveNumericComparisonCallChecker
|
||||
import org.jetbrains.kotlin.resolve.scopes.LexicalScope
|
||||
import org.jetbrains.kotlin.resolve.scopes.LexicalScopeKind
|
||||
import org.jetbrains.kotlin.types.*
|
||||
import org.jetbrains.kotlin.types.TypeUtils.NO_EXPECTED_TYPE
|
||||
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
|
||||
@@ -95,11 +98,52 @@ class PatternMatchingTypingVisitor internal constructor(facade: ExpressionTyping
|
||||
components.dataFlowAnalyzer.recordExpectedType(trace, expression, contextWithExpectedType.expectedType)
|
||||
|
||||
val contextBeforeSubject = contextWithExpectedType.replaceExpectedType(NO_EXPECTED_TYPE).replaceContextDependency(INDEPENDENT)
|
||||
// TODO :change scope according to the bound value in the when header
|
||||
val subjectExpression = expression.subjectExpression
|
||||
|
||||
val subjectTypeInfo = subjectExpression?.let { facade.getTypeInfo(it, contextBeforeSubject) }
|
||||
val contextAfterSubject = subjectTypeInfo?.let { contextBeforeSubject.replaceDataFlowInfo(it.dataFlowInfo) } ?: contextBeforeSubject
|
||||
// TODO change scope according to the bound value in the when header
|
||||
|
||||
val subjectExpression = expression.subjectExpression
|
||||
val subjectVariable = expression.subjectVariable
|
||||
|
||||
val subjectTypeInfo: KotlinTypeInfo?
|
||||
val scopeWithSubjectVariable: LexicalScope?
|
||||
|
||||
if (subjectVariable != null) {
|
||||
if (!components.languageVersionSettings.supportsFeature(LanguageFeature.VariableDeclarationInWhenSubject)) {
|
||||
trace.report(Errors.UNSUPPORTED_FEATURE.on(
|
||||
subjectVariable,
|
||||
Pair(LanguageFeature.VariableDeclarationInWhenSubject, components.languageVersionSettings)
|
||||
))
|
||||
}
|
||||
|
||||
scopeWithSubjectVariable = ExpressionTypingUtils.newWritableScopeImpl(contextBeforeSubject, LexicalScopeKind.WHEN, components.overloadChecker)
|
||||
|
||||
val (typeInfo, descriptor) = components.localVariableResolver.process(subjectVariable, contextBeforeSubject, contextBeforeSubject.scope, facade)
|
||||
// NB typeInfo returned by 'localVariableResolver.process(...)' treats local variable declaration as a statement,
|
||||
// so 'typeInfo' above it has type 'kotlin.Unit'.
|
||||
// Propagate declared variable type as a "subject expression" type.
|
||||
subjectTypeInfo = typeInfo.replaceType(descriptor.type)
|
||||
scopeWithSubjectVariable.addVariableDescriptor(descriptor)
|
||||
}
|
||||
else {
|
||||
scopeWithSubjectVariable = null
|
||||
|
||||
subjectTypeInfo = subjectExpression?.let { facade.getTypeInfo(it, contextBeforeSubject) }
|
||||
}
|
||||
|
||||
val contextAfterSubject = run {
|
||||
var context = contextBeforeSubject
|
||||
if (subjectTypeInfo != null) {
|
||||
context = context.replaceDataFlowInfo(subjectTypeInfo.dataFlowInfo)
|
||||
}
|
||||
if (scopeWithSubjectVariable != null) {
|
||||
context = context.replaceScope(scopeWithSubjectVariable)
|
||||
}
|
||||
context
|
||||
}
|
||||
|
||||
val contextWithExpectedTypeAndSubjectVariable = scopeWithSubjectVariable?.let { contextWithExpectedType.replaceScope(it) } ?:
|
||||
contextWithExpectedType
|
||||
|
||||
val subjectType = subjectTypeInfo?.type ?: ErrorUtils.createErrorType("Unknown type")
|
||||
val jumpOutPossibleInSubject: Boolean = subjectTypeInfo?.jumpOutPossible ?: false
|
||||
val subjectDataFlowValue = subjectExpression?.let {
|
||||
@@ -112,7 +156,7 @@ class PatternMatchingTypingVisitor internal constructor(facade: ExpressionTyping
|
||||
checkSmartCastsInSubjectIfRequired(expression, contextBeforeSubject, subjectType, possibleTypesForSubject)
|
||||
|
||||
val dataFlowInfoForEntries = analyzeConditionsInWhenEntries(expression, contextAfterSubject, subjectDataFlowValue, subjectType)
|
||||
val whenReturnType = inferTypeForWhenExpression(expression, contextWithExpectedType, contextAfterSubject, dataFlowInfoForEntries)
|
||||
val whenReturnType = inferTypeForWhenExpression(expression, contextWithExpectedTypeAndSubjectVariable, contextAfterSubject, dataFlowInfoForEntries)
|
||||
val whenResultValue = whenReturnType?.let { facade.components.dataFlowValueFactory.createDataFlowValue(expression, it, contextAfterSubject) }
|
||||
|
||||
val branchesTypeInfo =
|
||||
|
||||
@@ -35,6 +35,11 @@ public class KtWhenExpression extends KtExpressionImpl {
|
||||
return findChildrenByType(KtNodeTypes.WHEN_ENTRY);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public KtProperty getSubjectVariable() {
|
||||
return findChildByClass(KtProperty.class);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public KtExpression getSubjectExpression() {
|
||||
return findChildByClass(KtExpression.class);
|
||||
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
// !LANGUAGE: +VariableDeclarationInWhenSubject
|
||||
// !DIAGNOSTICS: -UNUSED_VARIABLE
|
||||
|
||||
fun foo(): Any = 42
|
||||
|
||||
fun test(x: Any) {
|
||||
val z1 = when (val y = foo()) {
|
||||
42 -> "Magic: $y, $x"
|
||||
else -> {
|
||||
"Not magic: $y, $x"
|
||||
}
|
||||
}
|
||||
val z2 = "Anyway, it was $<!UNRESOLVED_REFERENCE!>y<!>"
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
package
|
||||
|
||||
public fun foo(): kotlin.Any
|
||||
public fun test(/*0*/ x: kotlin.Any): kotlin.Unit
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
// !LANGUAGE: +VariableDeclarationInWhenSubject
|
||||
// !DIAGNOSTICS: -UNUSED_VARIABLE -UNUSED_PARAMETER
|
||||
|
||||
fun foo(): Any = 42
|
||||
|
||||
fun test(y: Any) {
|
||||
val z = when (val <!NAME_SHADOWING!>y<!> = foo()) {
|
||||
42 -> "Magic: $y"
|
||||
else -> "Not magic: $y"
|
||||
}
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
package
|
||||
|
||||
public fun foo(): kotlin.Any
|
||||
public fun test(/*0*/ y: kotlin.Any): kotlin.Unit
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
// !LANGUAGE: -VariableDeclarationInWhenSubject
|
||||
// !DIAGNOSTICS: -UNUSED_VARIABLE
|
||||
|
||||
fun foo(): Any = 42
|
||||
|
||||
fun test(x: Any) {
|
||||
// NB check that we still resolve 'y', even though current language version doesn' support variable declaration in when subject
|
||||
|
||||
val z1 = when (<!UNSUPPORTED_FEATURE!>val y = foo()<!>) {
|
||||
42 -> "Magic: $y, $x"
|
||||
else -> {
|
||||
"Not magic: $y, $x"
|
||||
}
|
||||
}
|
||||
val z2 = "Anyway, it was $<!UNRESOLVED_REFERENCE!>y<!>"
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
package
|
||||
|
||||
public fun foo(): kotlin.Any
|
||||
public fun test(/*0*/ x: kotlin.Any): kotlin.Unit
|
||||
@@ -22457,6 +22457,33 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
|
||||
public void testWhenWithNothingAndLambdas() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/when/whenWithNothingAndLambdas.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/diagnostics/tests/when/withSubjectVariable")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class WithSubjectVariable extends AbstractDiagnosticsTest {
|
||||
public void testAllFilesPresentInWithSubjectVariable() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/diagnostics/tests/when/withSubjectVariable"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true);
|
||||
}
|
||||
|
||||
@TestMetadata("invisibleOutsideOfWhen.kt")
|
||||
public void testInvisibleOutsideOfWhen() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/when/withSubjectVariable/invisibleOutsideOfWhen.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("shadowingOtherVariable.kt")
|
||||
public void testShadowingOtherVariable() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/when/withSubjectVariable/shadowingOtherVariable.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("unsupportedFeature.kt")
|
||||
public void testUnsupportedFeature() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/when/withSubjectVariable/unsupportedFeature.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Generated
+27
@@ -22457,6 +22457,33 @@ public class DiagnosticsUsingJavacTestGenerated extends AbstractDiagnosticsUsing
|
||||
public void testWhenWithNothingAndLambdas() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/when/whenWithNothingAndLambdas.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/diagnostics/tests/when/withSubjectVariable")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class WithSubjectVariable extends AbstractDiagnosticsUsingJavacTest {
|
||||
public void testAllFilesPresentInWithSubjectVariable() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/diagnostics/tests/when/withSubjectVariable"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true);
|
||||
}
|
||||
|
||||
@TestMetadata("invisibleOutsideOfWhen.kt")
|
||||
public void testInvisibleOutsideOfWhen() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/when/withSubjectVariable/invisibleOutsideOfWhen.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("shadowingOtherVariable.kt")
|
||||
public void testShadowingOtherVariable() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/when/withSubjectVariable/shadowingOtherVariable.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("unsupportedFeature.kt")
|
||||
public void testUnsupportedFeature() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/when/withSubjectVariable/unsupportedFeature.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -73,6 +73,7 @@ enum class LanguageFeature(
|
||||
ReadDeserializedContracts(KOTLIN_1_3),
|
||||
UseReturnsEffect(KOTLIN_1_3),
|
||||
UseCallsInPlaceEffect(KOTLIN_1_3),
|
||||
VariableDeclarationInWhenSubject(KOTLIN_1_3),
|
||||
AllowContractsForCustomFunctions(KOTLIN_1_3, kind = UNSTABLE_FEATURE),
|
||||
ProhibitLocalAnnotations(KOTLIN_1_3, kind = BUG_FIX),
|
||||
|
||||
|
||||
Reference in New Issue
Block a user