diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/PatternMatchingTypingVisitor.kt b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/PatternMatchingTypingVisitor.kt index 74f2a1aa34c..52ad6fb4df6 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/PatternMatchingTypingVisitor.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/PatternMatchingTypingVisitor.kt @@ -95,18 +95,19 @@ 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 - private var _dataFlowValue: DataFlowValue? = null - val dataFlowValue get() = _dataFlowValue!! + lateinit var dataFlowValue: DataFlowValue; private set fun initDataFlowValue(contextAfterSubject: ExpressionTypingContext, builtIns: KotlinBuiltIns) { - _dataFlowValue = createDataFlowValue(contextAfterSubject, builtIns) + dataFlowValue = createDataFlowValue(contextAfterSubject, builtIns) } val dataFlowInfo get() = typeInfo?.dataFlowInfo val jumpOutPossible get() = typeInfo?.jumpOutPossible ?: false + class Expression( val expression: KtExpression, typeInfo: KotlinTypeInfo @@ -128,8 +129,6 @@ class PatternMatchingTypingVisitor internal constructor(facade: ExpressionTyping typeInfo: KotlinTypeInfo, scopeWithSubject: LexicalScope ) : Subject(variable, typeInfo, scopeWithSubject) { - private val initializer = variable.initializer!! - override fun createDataFlowValue(contextAfterSubject: ExpressionTypingContext, builtIns: KotlinBuiltIns) = DataFlowValue( IdentifierInfo.Variable( @@ -140,18 +139,23 @@ class PatternMatchingTypingVisitor internal constructor(facade: ExpressionTyping descriptor.type ) - override fun makeValueArgument(): ValueArgument = - CallMaker.makeExternalValueArgument( - KtPsiFactory(variable.project, true).createExpression(variable.name!!), - initializer - ) + override fun makeValueArgument(): ValueArgument? = + variable.initializer?.let { + CallMaker.makeExternalValueArgument( + KtPsiFactory(variable.project, true).createExpression(variable.name!!), + it + ) + } - override val valueExpression: KtExpression - get() = initializer + override fun makeCalleeExpressionForSpecialCall(): KtExpression? = + KtPsiFactory(variable.project, true).createExpression(variable.name!!) + + override val valueExpression: KtExpression? + get() = variable.initializer } - class None : Subject(null, null, null) { + object None : Subject(null, null, null) { override fun createDataFlowValue(contextAfterSubject: ExpressionTypingContext, builtIns: KotlinBuiltIns) = DataFlowValue.nullValue(builtIns) @@ -160,19 +164,6 @@ class PatternMatchingTypingVisitor internal constructor(facade: ExpressionTyping override val valueExpression: KtExpression? get() = null } - - class Error( - element: KtElement?, - typeInfo: KotlinTypeInfo?, - scopeWithSubject: LexicalScope? - ) : Subject(element, typeInfo, scopeWithSubject) { - override fun createDataFlowValue(contextAfterSubject: ExpressionTypingContext, builtIns: KotlinBuiltIns): DataFlowValue = - DataFlowValue.nullValue(builtIns) - - override fun makeValueArgument(): ValueArgument? = null - - override val valueExpression: KtExpression? get() = null - } } fun visitWhenExpression( @@ -196,7 +187,7 @@ class PatternMatchingTypingVisitor internal constructor(facade: ExpressionTyping val subject = when { subjectVariable != null -> processVariableSubject(subjectVariable, contextBeforeSubject) subjectExpression != null -> Subject.Expression(subjectExpression, facade.getTypeInfo(subjectExpression, contextBeforeSubject)) - else -> Subject.None() + else -> Subject.None } val contextAfterSubject = run { @@ -225,7 +216,7 @@ class PatternMatchingTypingVisitor internal constructor(facade: ExpressionTyping checkSmartCastsInSubjectIfRequired(expression, contextBeforeSubject, subject.type, possibleTypesForSubject) val dataFlowInfoForEntries = analyzeConditionsInWhenEntries(expression, contextAfterSubject, subject) - val whenReturnType = inferTypeForWhenExpression(expression, contextWithExpectedTypeAndSubjectVariable, contextAfterSubject, dataFlowInfoForEntries) + val whenReturnType = inferTypeForWhenExpression(expression, subject, contextWithExpectedTypeAndSubjectVariable, contextAfterSubject, dataFlowInfoForEntries) val whenResultValue = whenReturnType?.let { facade.components.dataFlowValueFactory.createDataFlowValue(expression, it, contextAfterSubject) } val branchesTypeInfo = @@ -255,7 +246,6 @@ class PatternMatchingTypingVisitor internal constructor(facade: ExpressionTyping private fun processVariableSubject(subjectVariable: KtProperty, contextBeforeSubject: ExpressionTypingContext): Subject { val trace = contextBeforeSubject.trace - var hasIllegalDeclarationInSubject = false if (!components.languageVersionSettings.supportsFeature(LanguageFeature.VariableDeclarationInWhenSubject)) { trace.report(UNSUPPORTED_FEATURE.on( subjectVariable, @@ -264,6 +254,7 @@ class PatternMatchingTypingVisitor internal constructor(facade: ExpressionTyping } else { val illegalDeclarationString = when { + subjectVariable is KtDestructuringDeclaration -> "destructuring declaration" subjectVariable.isVar -> "var" subjectVariable.initializer == null -> "variable without initializer" subjectVariable.hasDelegateExpression() -> "delegated property" @@ -272,7 +263,6 @@ class PatternMatchingTypingVisitor internal constructor(facade: ExpressionTyping } if (illegalDeclarationString != null) { - hasIllegalDeclarationInSubject = true trace.report(Errors.ILLEGAL_DECLARATION_IN_WHEN_SUBJECT.on(subjectVariable, illegalDeclarationString)) } } @@ -290,36 +280,41 @@ class PatternMatchingTypingVisitor internal constructor(facade: ExpressionTyping // so 'typeInfo' above it has type 'kotlin.Unit'. // Propagate declared variable type as a "subject expression" type. val subjectTypeInfo = typeInfo.replaceType(descriptor.type) - return if (hasIllegalDeclarationInSubject) - Subject.Error(subjectVariable, subjectTypeInfo, scopeWithSubjectVariable) - else - Subject.Variable(subjectVariable, descriptor, subjectTypeInfo, scopeWithSubjectVariable) + + return Subject.Variable(subjectVariable, descriptor, subjectTypeInfo, scopeWithSubjectVariable) } private fun inferTypeForWhenExpression( - expression: KtWhenExpression, - contextWithExpectedType: ExpressionTypingContext, - contextAfterSubject: ExpressionTypingContext, - dataFlowInfoForEntries: List + expression: KtWhenExpression, + subject: Subject, + contextWithExpectedType: ExpressionTypingContext, + contextAfterSubject: ExpressionTypingContext, + dataFlowInfoForEntries: List ): KotlinType? { if (expression.entries.all { it.expression == null }) { return components.builtIns.unitType } val wrappedArgumentExpressions = wrapWhenEntryExpressionsAsSpecialCallArguments(expression) - val callForWhen = createCallForSpecialConstruction(expression, expression, wrappedArgumentExpressions) - val dataFlowInfoForArguments = - createDataFlowInfoForArgumentsOfWhenCall(callForWhen, contextAfterSubject.dataFlowInfo, dataFlowInfoForEntries) + val callForWhen = createCallForSpecialConstruction( + expression, + subject.makeCalleeExpressionForSpecialCall() ?: expression, + wrappedArgumentExpressions + ) + val dataFlowInfoForArguments = createDataFlowInfoForArgumentsOfWhenCall( + callForWhen, contextAfterSubject.dataFlowInfo, dataFlowInfoForEntries + ) val resolvedCall = components.controlStructureTypingUtils.resolveSpecialConstructionAsCall( - callForWhen, ResolveConstruct.WHEN, + callForWhen, + ResolveConstruct.WHEN, object : AbstractList() { override fun get(index: Int): String = "entry$index" override val size: Int get() = wrappedArgumentExpressions.size }, Collections.nCopies(wrappedArgumentExpressions.size, false), - contextWithExpectedType, dataFlowInfoForArguments - ) + contextWithExpectedType, + dataFlowInfoForArguments) return resolvedCall.resultingDescriptor.returnType } diff --git a/compiler/testData/diagnostics/tests/when/withSubjectVariable/shadowingOtherVariable.kt b/compiler/testData/diagnostics/tests/when/withSubjectVariable/shadowingOtherVariable.kt index 6cf9c214e6b..bbd5b1749fa 100644 --- a/compiler/testData/diagnostics/tests/when/withSubjectVariable/shadowingOtherVariable.kt +++ b/compiler/testData/diagnostics/tests/when/withSubjectVariable/shadowingOtherVariable.kt @@ -2,10 +2,26 @@ // !DIAGNOSTICS: -UNUSED_VARIABLE -UNUSED_PARAMETER fun foo(): Any = 42 +fun useInt(i: Int) {} -fun test(y: Any) { - val z = when (val y = foo()) { - 42 -> "Magic: $y" - else -> "Not magic: $y" +fun testShadowingParameter(y: Any) { + when (val y = foo()) { + else -> {} + } +} + +fun testShadowedInWhenBody(x: Any) { + when (val y = x) { + is String -> { + val y = y.length + useInt(y) + } + } +} + +fun testShadowinLocalVariable() { + val y = foo() + when (val y = foo()) { + else -> {} } } \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/when/withSubjectVariable/shadowingOtherVariable.txt b/compiler/testData/diagnostics/tests/when/withSubjectVariable/shadowingOtherVariable.txt index eaab2094128..3c7c9e99c97 100644 --- a/compiler/testData/diagnostics/tests/when/withSubjectVariable/shadowingOtherVariable.txt +++ b/compiler/testData/diagnostics/tests/when/withSubjectVariable/shadowingOtherVariable.txt @@ -1,4 +1,7 @@ package public fun foo(): kotlin.Any -public fun test(/*0*/ y: kotlin.Any): kotlin.Unit +public fun testShadowedInWhenBody(/*0*/ x: kotlin.Any): kotlin.Unit +public fun testShadowinLocalVariable(): kotlin.Unit +public fun testShadowingParameter(/*0*/ y: kotlin.Any): kotlin.Unit +public fun useInt(/*0*/ i: kotlin.Int): kotlin.Unit diff --git a/compiler/testData/diagnostics/tests/when/withSubjectVariable/subjectVariableInIsPattern.kt b/compiler/testData/diagnostics/tests/when/withSubjectVariable/subjectVariableInIsPattern.kt new file mode 100644 index 00000000000..ed1765401ca --- /dev/null +++ b/compiler/testData/diagnostics/tests/when/withSubjectVariable/subjectVariableInIsPattern.kt @@ -0,0 +1,8 @@ +// !LANGUAGE: +VariableDeclarationInWhenSubject +// !DIAGNOSTICS: -UNUSED_VARIABLE -UNUSED_PARAMETER + +fun test(x: Any) { + when (val y = x) { + is String -> {} + } +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/when/withSubjectVariable/subjectVariableInIsPattern.txt b/compiler/testData/diagnostics/tests/when/withSubjectVariable/subjectVariableInIsPattern.txt new file mode 100644 index 00000000000..435931c197a --- /dev/null +++ b/compiler/testData/diagnostics/tests/when/withSubjectVariable/subjectVariableInIsPattern.txt @@ -0,0 +1,3 @@ +package + +public fun test(/*0*/ x: kotlin.Any): kotlin.Unit diff --git a/compiler/testData/diagnostics/tests/when/withSubjectVariable/unsupportedVariableDeclarationsInWhenSubject.kt b/compiler/testData/diagnostics/tests/when/withSubjectVariable/unsupportedVariableDeclarationsInWhenSubject.kt index 4ef31f66f42..21c8ca8bbf5 100644 --- a/compiler/testData/diagnostics/tests/when/withSubjectVariable/unsupportedVariableDeclarationsInWhenSubject.kt +++ b/compiler/testData/diagnostics/tests/when/withSubjectVariable/unsupportedVariableDeclarationsInWhenSubject.kt @@ -2,8 +2,6 @@ // !DIAGNOSTICS: -UNUSED_VARIABLE -UNUSED_PARAMETER // WITH_RUNTIME -data class P(val x: Any, val y: Any) - fun foo(): Any = 42 fun String.bar(): Any = 42 @@ -15,26 +13,16 @@ fun testSimpleValInWhenSubject() { fun testValWithoutInitializerWhenSubject() { when (val y: Any) { - is String -> y.length + is String -> y.length } } fun testVarInWhenSubject() { when (var y = foo()) { - is String -> y.length + is String -> y.length } } -fun testDestructuringInWhenSubject() { - when (val (y1, y2) = P(1, 2)) { - } -} - -fun testExtensionValInWhenSubject() { - when (val String.len get() = bar()) { - } -} - fun testDelegatedValInWhenSubject() { when (val y by lazy { 42 }) { } diff --git a/compiler/testData/diagnostics/tests/when/withSubjectVariable/unsupportedVariableDeclarationsInWhenSubject.txt b/compiler/testData/diagnostics/tests/when/withSubjectVariable/unsupportedVariableDeclarationsInWhenSubject.txt index 8f1369a93af..1de50387b7b 100644 --- a/compiler/testData/diagnostics/tests/when/withSubjectVariable/unsupportedVariableDeclarationsInWhenSubject.txt +++ b/compiler/testData/diagnostics/tests/when/withSubjectVariable/unsupportedVariableDeclarationsInWhenSubject.txt @@ -2,21 +2,7 @@ package public fun foo(): kotlin.Any public fun testDelegatedValInWhenSubject(): kotlin.Unit -public fun testDestructuringInWhenSubject(): kotlin.Unit -public fun testExtensionValInWhenSubject(): kotlin.Unit public fun testSimpleValInWhenSubject(): kotlin.Unit public fun testValWithoutInitializerWhenSubject(): kotlin.Unit public fun testVarInWhenSubject(): kotlin.Unit public fun kotlin.String.bar(): kotlin.Any - -public final data class P { - public constructor P(/*0*/ x: kotlin.Any, /*1*/ y: kotlin.Any) - public final val x: kotlin.Any - public final val y: kotlin.Any - public final operator /*synthesized*/ fun component1(): kotlin.Any - public final operator /*synthesized*/ fun component2(): kotlin.Any - public final /*synthesized*/ fun copy(/*0*/ x: kotlin.Any = ..., /*1*/ y: kotlin.Any = ...): P - public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean - public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int - public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String -} diff --git a/compiler/tests-common/tests/org/jetbrains/kotlin/checkers/AbstractDiagnosticsTest.kt b/compiler/tests-common/tests/org/jetbrains/kotlin/checkers/AbstractDiagnosticsTest.kt index ca4677685db..f49956ac022 100644 --- a/compiler/tests-common/tests/org/jetbrains/kotlin/checkers/AbstractDiagnosticsTest.kt +++ b/compiler/tests-common/tests/org/jetbrains/kotlin/checkers/AbstractDiagnosticsTest.kt @@ -8,6 +8,7 @@ package org.jetbrains.kotlin.checkers import com.intellij.openapi.util.io.FileUtil import com.intellij.openapi.util.text.StringUtil import com.intellij.psi.search.GlobalSearchScope +import junit.framework.TestCase import org.jetbrains.kotlin.analyzer.AnalysisResult import org.jetbrains.kotlin.analyzer.common.CommonAnalyzerFacade import org.jetbrains.kotlin.cli.jvm.compiler.NoScopeRecordCliBindingTrace @@ -53,6 +54,7 @@ import org.jetbrains.kotlin.test.util.DescriptorValidator import org.jetbrains.kotlin.test.util.RecursiveDescriptorComparator import org.jetbrains.kotlin.test.util.RecursiveDescriptorComparator.RECURSIVE import org.jetbrains.kotlin.test.util.RecursiveDescriptorComparator.RECURSIVE_ALL +import org.jetbrains.kotlin.utils.addToStdlib.cast import org.jetbrains.kotlin.utils.keysToMap import org.junit.Assert import java.io.File diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java index 190880be37a..6bbb00518c9 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -22496,6 +22496,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { doTest(fileName); } + @TestMetadata("subjectVariableInIsPattern.kt") + public void testSubjectVariableInIsPattern() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/when/withSubjectVariable/subjectVariableInIsPattern.kt"); + doTest(fileName); + } + @TestMetadata("unsupportedFeature.kt") public void testUnsupportedFeature() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/when/withSubjectVariable/unsupportedFeature.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java index 058538d3bd9..226e899867c 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java @@ -22496,6 +22496,12 @@ public class DiagnosticsUsingJavacTestGenerated extends AbstractDiagnosticsUsing doTest(fileName); } + @TestMetadata("subjectVariableInIsPattern.kt") + public void testSubjectVariableInIsPattern() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/when/withSubjectVariable/subjectVariableInIsPattern.kt"); + doTest(fileName); + } + @TestMetadata("unsupportedFeature.kt") public void testUnsupportedFeature() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/when/withSubjectVariable/unsupportedFeature.kt");