From 952f67dafc83b9256cc7473adb3f150cfe8e31fb Mon Sep 17 00:00:00 2001 From: Dmitry Savvinov Date: Fri, 4 May 2018 16:13:51 +0300 Subject: [PATCH] Fix bogus smartcast on enum entry members Previously, enum entries were treated by the data-flow subsystem similar to other class/singletons. As a consequence, calls like 'Enum.ENTRY.property' had IdentifierInfo of 'property'. However, specially for enum entries, descriptor of 'property' is one and the same for all entries. It means that from the data-flow point of view, 'Enum.ONE.property' and 'Enum.TWO.property' are *one and the same data-flow values*. It could obviously lead to some bogus smartcasts, so this commit introduces separate IdentifierInfo.EnumEntry and uses it to build proper qualified values. ^KT-20772 Fixed --- .../calls/smartcasts/IdentifierInfo.kt | 14 +++++++++++++- .../smartCasts/enumEntryMembers_after.kt | 19 +++++++++++++++++++ .../smartCasts/enumEntryMembers_before.kt | 19 +++++++++++++++++++ .../checkers/DiagnosticsTestGenerated.java | 10 ++++++++++ .../DiagnosticsUsingJavacTestGenerated.java | 10 ++++++++++ .../kotlin/config/LanguageVersionSettings.kt | 1 + 6 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 compiler/testData/diagnostics/tests/smartCasts/enumEntryMembers_after.kt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/enumEntryMembers_before.kt diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/IdentifierInfo.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/IdentifierInfo.kt index b39e2873b6c..24609e50c07 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/IdentifierInfo.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/IdentifierInfo.kt @@ -16,6 +16,7 @@ package org.jetbrains.kotlin.resolve.calls.smartcasts +import org.jetbrains.kotlin.config.LanguageFeature import org.jetbrains.kotlin.config.LanguageVersionSettings import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.lexer.KtToken @@ -77,6 +78,10 @@ interface IdentifierInfo { override fun toString() = descriptor.toString() } + data class EnumEntry(val descriptor: ClassDescriptor) : IdentifierInfo { + override val kind: DataFlowValue.Kind = STABLE_VALUE + } + class Qualified( val receiverInfo: IdentifierInfo, val selectorInfo: IdentifierInfo, @@ -229,7 +234,14 @@ private fun getIdForSimpleNameExpression( } } - is PackageViewDescriptor, is ClassDescriptor -> IdentifierInfo.PackageOrClass(declarationDescriptor) + is ClassDescriptor -> { + if (declarationDescriptor.kind == ClassKind.ENUM_ENTRY && languageVersionSettings.supportsFeature(LanguageFeature.SoundSmartcastForEnumEntries)) + IdentifierInfo.EnumEntry(declarationDescriptor) + else + IdentifierInfo.PackageOrClass(declarationDescriptor) + } + + is PackageViewDescriptor -> IdentifierInfo.PackageOrClass(declarationDescriptor) else -> IdentifierInfo.NO } diff --git a/compiler/testData/diagnostics/tests/smartCasts/enumEntryMembers_after.kt b/compiler/testData/diagnostics/tests/smartCasts/enumEntryMembers_after.kt new file mode 100644 index 00000000000..aafe29bab3f --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/enumEntryMembers_after.kt @@ -0,0 +1,19 @@ +// !LANGUAGE: +SoundSmartcastForEnumEntries +// !DIAGNOSTICS: -UNUSED_VARIABLE +// SKIP_TXT + +enum class Message(val text: String?) { + HELLO("hello"), + WORLD("world"), + NOTHING(null) +} + +fun printMessages() { + Message.HELLO.text!! + Message.HELLO.text.length + + Message.NOTHING.text.length + + Message.NOTHING.text!! + Message.NOTHING.text.length +} diff --git a/compiler/testData/diagnostics/tests/smartCasts/enumEntryMembers_before.kt b/compiler/testData/diagnostics/tests/smartCasts/enumEntryMembers_before.kt new file mode 100644 index 00000000000..b584253029b --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/enumEntryMembers_before.kt @@ -0,0 +1,19 @@ +// !LANGUAGE: -SoundSmartcastForEnumEntries +// !DIAGNOSTICS: -UNUSED_VARIABLE +// SKIP_TXT + +enum class Message(val text: String?) { + HELLO("hello"), + WORLD("world"), + NOTHING(null) +} + +fun printMessages() { + Message.HELLO.text!! + Message.HELLO.text.length + + Message.NOTHING.text.length + + Message.NOTHING.text!! + Message.NOTHING.text.length +} diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java index fad98753997..311648915fa 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -18849,6 +18849,16 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { runTest("compiler/testData/diagnostics/tests/smartCasts/elvisRHS.kt"); } + @TestMetadata("enumEntryMembers_after.kt") + public void testEnumEntryMembers_after() throws Exception { + runTest("compiler/testData/diagnostics/tests/smartCasts/enumEntryMembers_after.kt"); + } + + @TestMetadata("enumEntryMembers_before.kt") + public void testEnumEntryMembers_before() throws Exception { + runTest("compiler/testData/diagnostics/tests/smartCasts/enumEntryMembers_before.kt"); + } + @TestMetadata("equals.kt") public void testEquals() throws Exception { runTest("compiler/testData/diagnostics/tests/smartCasts/equals.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java index a2a3caf56f7..2acbf4b0a13 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java @@ -18849,6 +18849,16 @@ public class DiagnosticsUsingJavacTestGenerated extends AbstractDiagnosticsUsing runTest("compiler/testData/diagnostics/tests/smartCasts/elvisRHS.kt"); } + @TestMetadata("enumEntryMembers_after.kt") + public void testEnumEntryMembers_after() throws Exception { + runTest("compiler/testData/diagnostics/tests/smartCasts/enumEntryMembers_after.kt"); + } + + @TestMetadata("enumEntryMembers_before.kt") + public void testEnumEntryMembers_before() throws Exception { + runTest("compiler/testData/diagnostics/tests/smartCasts/enumEntryMembers_before.kt"); + } + @TestMetadata("equals.kt") public void testEquals() throws Exception { runTest("compiler/testData/diagnostics/tests/smartCasts/equals.kt"); diff --git a/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt b/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt index cac0e88d16c..c5f6770b9fc 100644 --- a/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt +++ b/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt @@ -84,6 +84,7 @@ enum class LanguageFeature( NoConstantValueAttributeForNonConstVals(KOTLIN_1_3, kind = BUG_FIX), NormalizeConstructorCalls(KOTLIN_1_3), StrictJavaNullabilityAssertions(KOTLIN_1_3, kind = BUG_FIX), + SoundSmartcastForEnumEntries(KOTLIN_1_3, kind = BUG_FIX), RestrictReturnStatementTarget(KOTLIN_1_4, kind = BUG_FIX), ProperIeee754Comparisons(sinceVersion = null, defaultState = State.DISABLED, kind = BUG_FIX),