diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java index 580dfddddb3..ab87f8c041e 100644 --- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java +++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java @@ -10355,6 +10355,12 @@ public class DiagnosisCompilerTestFE10TestdataTestGenerated extends AbstractDiag runTest("compiler/testData/diagnostics/tests/enum/entries/genericEntriesPropertyClash.kt"); } + @Test + @TestMetadata("nameShadowingOfExternallyDefinedEntries.kt") + public void testNameShadowingOfExternallyDefinedEntries() throws Exception { + runTest("compiler/testData/diagnostics/tests/enum/entries/nameShadowingOfExternallyDefinedEntries.kt"); + } + @Test @TestMetadata("redeclarationOfEnumEntriesNameWithIntrinsic.kt") public void testRedeclarationOfEnumEntriesNameWithIntrinsic() throws Exception { diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java index ff195d49b5b..ec0f66d803e 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java @@ -10361,6 +10361,12 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti runTest("compiler/testData/diagnostics/tests/enum/entries/genericEntriesPropertyClash.kt"); } + @Test + @TestMetadata("nameShadowingOfExternallyDefinedEntries.kt") + public void testNameShadowingOfExternallyDefinedEntries() throws Exception { + runTest("compiler/testData/diagnostics/tests/enum/entries/nameShadowingOfExternallyDefinedEntries.kt"); + } + @Test @TestMetadata("redeclarationOfEnumEntriesNameWithIntrinsic.kt") public void testRedeclarationOfEnumEntriesNameWithIntrinsic() throws Exception { diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java index b056755ecc9..bf6dba2d3ac 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java @@ -10355,6 +10355,12 @@ public class FirOldFrontendDiagnosticsWithLightTreeTestGenerated extends Abstrac runTest("compiler/testData/diagnostics/tests/enum/entries/genericEntriesPropertyClash.kt"); } + @Test + @TestMetadata("nameShadowingOfExternallyDefinedEntries.kt") + public void testNameShadowingOfExternallyDefinedEntries() throws Exception { + runTest("compiler/testData/diagnostics/tests/enum/entries/nameShadowingOfExternallyDefinedEntries.kt"); + } + @Test @TestMetadata("redeclarationOfEnumEntriesNameWithIntrinsic.kt") public void testRedeclarationOfEnumEntriesNameWithIntrinsic() throws Exception { diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java index 64a3ed016c8..fb40bbf8591 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java @@ -143,6 +143,7 @@ public interface Errors { DiagnosticFactory1 DEPRECATED_ACCESS_TO_ENUM_COMPANION_PROPERTY = DiagnosticFactory1.create(WARNING); DiagnosticFactory0 DEPRECATED_ACCESS_TO_ENUM_ENTRY_COMPANION_PROPERTY = DiagnosticFactory0.create(WARNING); + DiagnosticFactory0 DEPRECATED_ACCESS_TO_ENTRY_PROPERTY_FROM_ENUM = DiagnosticFactory0.create(WARNING); DiagnosticFactory0 DEPRECATED_DECLARATION_OF_ENUM_ENTRY = DiagnosticFactory0.create(WARNING); DiagnosticFactory2 DEPRECATED_RESOLVE_WITH_AMBIGUOUS_ENUM_ENTRY = DiagnosticFactory2.create(WARNING); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java index a6596e123ae..08e7afbc9f2 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java @@ -74,6 +74,7 @@ public class DefaultErrorMessages { MAP.put(DEPRECATED_ACCESS_BY_SHORT_NAME, "Access to this type by short name is deprecated, and soon is going to be removed. Please, add explicit qualifier or import", NAME); MAP.put(DEPRECATED_ACCESS_TO_ENUM_COMPANION_PROPERTY, "Ambiguous access to companion''s property ''{0}'' in enum is deprecated. Please, add explicit Companion qualifier to the class name", NAME); MAP.put(DEPRECATED_ACCESS_TO_ENUM_ENTRY_COMPANION_PROPERTY, "Ambiguous access to companion''s property 'entries' in enum is deprecated. Please, add explicit Companion qualifier to the class name"); + MAP.put(DEPRECATED_ACCESS_TO_ENTRY_PROPERTY_FROM_ENUM, "Ambiguous access to property 'entries' from enum is deprecated. Please, add explicit qualifier to the call"); MAP.put(DEPRECATED_DECLARATION_OF_ENUM_ENTRY, "Conflicting declarations: enum entry 'entries' and the property 'Enum.entries' (KT-48872). Please, rename the enum entry declaration"); MAP.put(DEPRECATED_RESOLVE_WITH_AMBIGUOUS_ENUM_ENTRY, "Ambiguous access to property ''{0}'' is deprecated because similar enum entry ''{1}'' is available. Please add explicit named import or use fully qualified name", FQ_NAME, FQ_NAME); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/CustomEnumEntriesMigrationCallChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/CustomEnumEntriesMigrationCallChecker.kt index 0b44a9e51d5..ef992352a93 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/CustomEnumEntriesMigrationCallChecker.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/CustomEnumEntriesMigrationCallChecker.kt @@ -10,11 +10,13 @@ import org.jetbrains.kotlin.builtins.StandardNames import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.PropertyDescriptor import org.jetbrains.kotlin.diagnostics.Errors +import org.jetbrains.kotlin.psi.KtClass +import org.jetbrains.kotlin.psi.KtDotQualifiedExpression +import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall import org.jetbrains.kotlin.resolve.descriptorUtil.isCompanionObject import org.jetbrains.kotlin.resolve.scopes.receivers.ClassValueReceiver -import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue object CustomEnumEntriesMigrationCallChecker : CallChecker { override fun check(resolvedCall: ResolvedCall<*>, reportOn: PsiElement, context: CallCheckerContext) { @@ -26,6 +28,8 @@ object CustomEnumEntriesMigrationCallChecker : CallChecker { resolvedCall.isCallViaCompanionOnEnumClassQualifier(descriptor) ) { context.trace.report(Errors.DEPRECATED_ACCESS_TO_ENUM_ENTRY_COMPANION_PROPERTY.on(reportOn)) + } else if (descriptor.isCallToExternalEntriesInsideEnum(reportOn)) { + context.trace.report(Errors.DEPRECATED_ACCESS_TO_ENTRY_PROPERTY_FROM_ENUM.on(reportOn)) } } @@ -43,4 +47,10 @@ object CustomEnumEntriesMigrationCallChecker : CallChecker { return dispatchReceiver.isQualifierFor(grandParent) } + + private fun PropertyDescriptor.isCallToExternalEntriesInsideEnum(contextExpression: PsiElement): Boolean { + return !DescriptorUtils.isEnumClass(this.containingDeclaration) && + contextExpression.parent !is KtDotQualifiedExpression && + contextExpression.parentsWithSelf.any { it is KtClass && it.isEnum() } + } } \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/enum/entries/nameShadowingOfExternallyDefinedEntries.fir.kt b/compiler/testData/diagnostics/tests/enum/entries/nameShadowingOfExternallyDefinedEntries.fir.kt new file mode 100644 index 00000000000..0d0d7644310 --- /dev/null +++ b/compiler/testData/diagnostics/tests/enum/entries/nameShadowingOfExternallyDefinedEntries.fir.kt @@ -0,0 +1,39 @@ +// !LANGUAGE: -EnumEntries + +package pckg + +val entries = "E" + +enum class E { + ; + + fun foo() { + entries + pckg.entries + } +} + +class A { + enum class E { + ; + + class B { + fun foo() { + entries + pckg.entries + } + } + + class C { + val entries = 0 + + fun foo() { + // technically, this warning is incorrect but I believe it's OK to report anyway + // first, logic in the compiler will be complicated if we'll try to avoid reporting warnings here + // second, this code smells, it'd be better to use qualifiers here anyway + entries + this.entries + } + } + } +} diff --git a/compiler/testData/diagnostics/tests/enum/entries/nameShadowingOfExternallyDefinedEntries.kt b/compiler/testData/diagnostics/tests/enum/entries/nameShadowingOfExternallyDefinedEntries.kt new file mode 100644 index 00000000000..5117f9b4dcf --- /dev/null +++ b/compiler/testData/diagnostics/tests/enum/entries/nameShadowingOfExternallyDefinedEntries.kt @@ -0,0 +1,39 @@ +// !LANGUAGE: -EnumEntries + +package pckg + +val entries = "E" + +enum class E { + ; + + fun foo() { + entries + pckg.entries + } +} + +class A { + enum class E { + ; + + class B { + fun foo() { + entries + pckg.entries + } + } + + class C { + val entries = 0 + + fun foo() { + // technically, this warning is incorrect but I believe it's OK to report anyway + // first, logic in the compiler will be complicated if we'll try to avoid reporting warnings here + // second, this code smells, it'd be better to use qualifiers here anyway + entries + this.entries + } + } + } +} diff --git a/compiler/testData/diagnostics/tests/enum/entries/nameShadowingOfExternallyDefinedEntries.txt b/compiler/testData/diagnostics/tests/enum/entries/nameShadowingOfExternallyDefinedEntries.txt new file mode 100644 index 00000000000..e39fe5ff58f --- /dev/null +++ b/compiler/testData/diagnostics/tests/enum/entries/nameShadowingOfExternallyDefinedEntries.txt @@ -0,0 +1,64 @@ +package + +package pckg { + public val entries: kotlin.String = "E" + + public final class A { + public constructor A() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + + public final enum class E : kotlin.Enum { + private constructor E() + @kotlin.internal.IntrinsicConstEvaluation public final override /*1*/ /*fake_override*/ val name: kotlin.String + public final override /*1*/ /*fake_override*/ val ordinal: kotlin.Int + protected final override /*1*/ /*fake_override*/ fun clone(): kotlin.Any + public final override /*1*/ /*fake_override*/ fun compareTo(/*0*/ other: pckg.A.E): kotlin.Int + public final override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + protected/*protected and package*/ final override /*1*/ /*fake_override*/ /*isHiddenForResolutionEverywhereBesideSupercalls*/ fun finalize(): kotlin.Unit + public final override /*1*/ /*fake_override*/ /*isHiddenForResolutionEverywhereBesideSupercalls*/ fun getDeclaringClass(): java.lang.Class! + public final override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + + public final class B { + public constructor B() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public final fun foo(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } + + public final class C { + public constructor C() + public final val entries: kotlin.Int = 0 + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public final fun foo(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } + + // Static members + public final /*synthesized*/ fun valueOf(/*0*/ value: kotlin.String): pckg.A.E + public final /*synthesized*/ fun values(): kotlin.Array + } + } + + public final enum class E : kotlin.Enum { + private constructor E() + @kotlin.internal.IntrinsicConstEvaluation public final override /*1*/ /*fake_override*/ val name: kotlin.String + public final override /*1*/ /*fake_override*/ val ordinal: kotlin.Int + protected final override /*1*/ /*fake_override*/ fun clone(): kotlin.Any + public final override /*1*/ /*fake_override*/ fun compareTo(/*0*/ other: pckg.E): kotlin.Int + public final override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + protected/*protected and package*/ final override /*1*/ /*fake_override*/ /*isHiddenForResolutionEverywhereBesideSupercalls*/ fun finalize(): kotlin.Unit + public final fun foo(): kotlin.Unit + public final override /*1*/ /*fake_override*/ /*isHiddenForResolutionEverywhereBesideSupercalls*/ fun getDeclaringClass(): java.lang.Class! + public final override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + + // Static members + public final /*synthesized*/ fun valueOf(/*0*/ value: kotlin.String): pckg.E + public final /*synthesized*/ fun values(): kotlin.Array + } +} diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java index 5054e7544bf..68b48584334 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java @@ -10361,6 +10361,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest { runTest("compiler/testData/diagnostics/tests/enum/entries/genericEntriesPropertyClash.kt"); } + @Test + @TestMetadata("nameShadowingOfExternallyDefinedEntries.kt") + public void testNameShadowingOfExternallyDefinedEntries() throws Exception { + runTest("compiler/testData/diagnostics/tests/enum/entries/nameShadowingOfExternallyDefinedEntries.kt"); + } + @Test @TestMetadata("redeclarationOfEnumEntriesNameWithIntrinsic.kt") public void testRedeclarationOfEnumEntriesNameWithIntrinsic() throws Exception {