From 46b297477c18e604f444eff8a938e980a7a076f8 Mon Sep 17 00:00:00 2001 From: "Denis.Zharkov" Date: Wed, 16 Jun 2021 18:58:34 +0300 Subject: [PATCH] Deprecate ambiguous cases in FE 1.0: companion property vs enum entry ^KT-37591 Fixed --- ...irOldFrontendDiagnosticsTestGenerated.java | 6 +++ ...DiagnosticsWithLightTreeTestGenerated.java | 6 +++ .../jetbrains/kotlin/diagnostics/Errors.java | 2 + .../rendering/DefaultErrorMessages.java | 1 + .../resolve/PlatformConfiguratorBase.kt | 2 +- ...EnumEntryVsCompanionPriorityCallChecker.kt | 52 +++++++++++++++++++ .../resolve/propertyInCompanionOfEnum.fir.kt | 40 ++++++++++++++ .../resolve/propertyInCompanionOfEnum.kt | 40 ++++++++++++++ .../test/runners/DiagnosticTestGenerated.java | 6 +++ ...CompilerTestFE10TestdataTestGenerated.java | 5 ++ 10 files changed, 159 insertions(+), 1 deletion(-) create mode 100644 compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/EnumEntryVsCompanionPriorityCallChecker.kt create mode 100644 compiler/testData/diagnostics/tests/resolve/propertyInCompanionOfEnum.fir.kt create mode 100644 compiler/testData/diagnostics/tests/resolve/propertyInCompanionOfEnum.kt 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 e33997040d4..c94950461fc 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 @@ -23273,6 +23273,12 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti runTest("compiler/testData/diagnostics/tests/resolve/parameterAsDefaultValueInLocalFunction.kt"); } + @Test + @TestMetadata("propertyInCompanionOfEnum.kt") + public void testPropertyInCompanionOfEnum() throws Exception { + runTest("compiler/testData/diagnostics/tests/resolve/propertyInCompanionOfEnum.kt"); + } + @Test @TestMetadata("resolveAnnotatedLambdaArgument.kt") public void testResolveAnnotatedLambdaArgument() 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 d1db4096d64..006721dd9d8 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 @@ -23273,6 +23273,12 @@ public class FirOldFrontendDiagnosticsWithLightTreeTestGenerated extends Abstrac runTest("compiler/testData/diagnostics/tests/resolve/parameterAsDefaultValueInLocalFunction.kt"); } + @Test + @TestMetadata("propertyInCompanionOfEnum.kt") + public void testPropertyInCompanionOfEnum() throws Exception { + runTest("compiler/testData/diagnostics/tests/resolve/propertyInCompanionOfEnum.kt"); + } + @Test @TestMetadata("resolveAnnotatedLambdaArgument.kt") public void testResolveAnnotatedLambdaArgument() 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 876fa098883..2a2e4682b7f 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java @@ -128,6 +128,8 @@ public interface Errors { DiagnosticFactory3 INVISIBLE_MEMBER = DiagnosticFactory3.create(ERROR, CALL_ELEMENT); DiagnosticFactory1 DEPRECATED_ACCESS_BY_SHORT_NAME = DiagnosticFactory1.create(WARNING); + DiagnosticFactory1 DEPRECATED_ACCESS_TO_ENUM_COMPANION_PROPERTY = DiagnosticFactory1.create(WARNING); + DiagnosticFactory1 PROTECTED_CONSTRUCTOR_NOT_IN_SUPER_CALL = DiagnosticFactory1.create(ERROR); // Exposed visibility group 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 0ee986e3840..7919f6459db 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java @@ -71,6 +71,7 @@ public class DefaultErrorMessages { MAP.put(INVISIBLE_REFERENCE, "Cannot access ''{0}'': it is {1} in {2}", NAME, VISIBILITY, NAME_OF_CONTAINING_DECLARATION_OR_FILE); MAP.put(INVISIBLE_MEMBER, "Cannot access ''{0}'': it is {1} in {2}", NAME, VISIBILITY, NAME_OF_CONTAINING_DECLARATION_OR_FILE); 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(PROTECTED_CONSTRUCTOR_NOT_IN_SUPER_CALL, "Protected constructor ''{0}'' from other classes can only be used in super-call", Renderers.SHORT_NAMES_IN_TYPES); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/PlatformConfiguratorBase.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/PlatformConfiguratorBase.kt index b2a9b418640..7532a18a259 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/PlatformConfiguratorBase.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/PlatformConfiguratorBase.kt @@ -60,7 +60,7 @@ private val DEFAULT_CALL_CHECKERS = listOf( MissingDependencySupertypeChecker.ForCalls, AbstractClassInstantiationChecker, SuspendConversionCallChecker, UnitConversionCallChecker, FunInterfaceConstructorReferenceChecker, NullableExtensionOperatorWithSafeCallChecker, ReferencingToUnderscoreNamedParameterOfCatchBlockChecker, VarargWrongExecutionOrderChecker, SelfCallInNestedObjectConstructorChecker, - NewSchemeOfIntegerOperatorResolutionChecker + NewSchemeOfIntegerOperatorResolutionChecker, EnumEntryVsCompanionPriorityCallChecker, ) private val DEFAULT_TYPE_CHECKERS = emptyList() private val DEFAULT_CLASSIFIER_USAGE_CHECKERS = listOf( diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/EnumEntryVsCompanionPriorityCallChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/EnumEntryVsCompanionPriorityCallChecker.kt new file mode 100644 index 00000000000..9e79ab6e25a --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/EnumEntryVsCompanionPriorityCallChecker.kt @@ -0,0 +1,52 @@ +/* + * Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.resolve.calls.checkers + +import com.intellij.psi.PsiElement +import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.descriptors.ClassKind +import org.jetbrains.kotlin.descriptors.PropertyDescriptor +import org.jetbrains.kotlin.diagnostics.Errors +import org.jetbrains.kotlin.incremental.components.NoLookupLocation +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.psi.KtNameReferenceExpression +import org.jetbrains.kotlin.psi.KtQualifiedExpression +import org.jetbrains.kotlin.psi.KtSimpleNameExpression +import org.jetbrains.kotlin.resolve.DescriptorEquivalenceForOverrides +import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall +import org.jetbrains.kotlin.resolve.scopes.receivers.ClassValueReceiver +import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue + +object EnumEntryVsCompanionPriorityCallChecker : CallChecker { + override fun check(resolvedCall: ResolvedCall<*>, reportOn: PsiElement, context: CallCheckerContext) { + val descriptor = resolvedCall.candidateDescriptor + if (descriptor !is PropertyDescriptor) return + val propertyName = descriptor.name + + val containingDescriptor = descriptor.containingDeclaration + if (containingDescriptor !is ClassDescriptor || !containingDescriptor.isCompanionObject) return + + val grandParent = containingDescriptor.containingDeclaration + + if (grandParent is ClassDescriptor && + grandParent.kind == ClassKind.ENUM_CLASS && + grandParent.containsEntryWithName(propertyName) && + resolvedCall.dispatchReceiver.isQualifierFor(grandParent)) { + context.resolutionContext.trace.report(Errors.DEPRECATED_ACCESS_TO_ENUM_COMPANION_PROPERTY.on(reportOn, descriptor)) + } + } + + private fun ClassDescriptor.containsEntryWithName(name: Name): Boolean { + val foundDescriptor = unsubstitutedMemberScope.getContributedClassifier(name, NoLookupLocation.FOR_ALREADY_TRACKED) + return foundDescriptor is ClassDescriptor && foundDescriptor.kind == ClassKind.ENUM_ENTRY + } + + private fun ReceiverValue?.isQualifierFor(classDescriptor: ClassDescriptor): Boolean { + if (this !is ClassValueReceiver) return false + val thisClass = this.classQualifier.descriptor as? ClassDescriptor ?: return false + return thisClass.typeConstructor == classDescriptor.typeConstructor + } +} diff --git a/compiler/testData/diagnostics/tests/resolve/propertyInCompanionOfEnum.fir.kt b/compiler/testData/diagnostics/tests/resolve/propertyInCompanionOfEnum.fir.kt new file mode 100644 index 00000000000..4cd10c29baa --- /dev/null +++ b/compiler/testData/diagnostics/tests/resolve/propertyInCompanionOfEnum.fir.kt @@ -0,0 +1,40 @@ +// !CHECK_TYPE +// SKIP_TXT + +// FILE: test.kt +package test +enum class E { + Entry; + companion object { + val Entry = "" + val NotEntry = "" + } +} + +// FILE: main.kt + +import test.E.Entry +import test.E.Companion as W +import test.E as U +import test.E + +fun foo() { + E.Entry checkType { _() } + E.Entry checkType { _() } + E.Companion.Entry checkType { _() } + E.NotEntry checkType { _() } + Entry checkType { _() } + + W.Entry checkType { _() } +} + +// FILE: Aliased.kt + +import test.E as U + +fun bar() { + U.Entry checkType { _<E>() } + U.Entry checkType { _() } + U.Companion.Entry checkType { _() } + U.NotEntry checkType { _() } +} diff --git a/compiler/testData/diagnostics/tests/resolve/propertyInCompanionOfEnum.kt b/compiler/testData/diagnostics/tests/resolve/propertyInCompanionOfEnum.kt new file mode 100644 index 00000000000..63a0f0e7d9b --- /dev/null +++ b/compiler/testData/diagnostics/tests/resolve/propertyInCompanionOfEnum.kt @@ -0,0 +1,40 @@ +// !CHECK_TYPE +// SKIP_TXT + +// FILE: test.kt +package test +enum class E { + Entry; + companion object { + val Entry = "" + val NotEntry = "" + } +} + +// FILE: main.kt + +import test.E.Entry +import test.E.Companion as W +import test.E as U +import test.E + +fun foo() { + E.Entry checkType { _() } + E.Entry checkType { _() } + E.Companion.Entry checkType { _() } + E.NotEntry checkType { _() } + Entry checkType { _() } + + W.Entry checkType { _() } +} + +// FILE: Aliased.kt + +import test.E as U + +fun bar() { + U.Entry checkType { _<E>() } + U.Entry checkType { _() } + U.Companion.Entry checkType { _() } + U.NotEntry checkType { _() } +} 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 5e8f1f716b8..f0171dccfb8 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 @@ -23285,6 +23285,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest { runTest("compiler/testData/diagnostics/tests/resolve/parameterAsDefaultValueInLocalFunction.kt"); } + @Test + @TestMetadata("propertyInCompanionOfEnum.kt") + public void testPropertyInCompanionOfEnum() throws Exception { + runTest("compiler/testData/diagnostics/tests/resolve/propertyInCompanionOfEnum.kt"); + } + @Test @TestMetadata("resolveAnnotatedLambdaArgument.kt") public void testResolveAnnotatedLambdaArgument() throws Exception { diff --git a/idea/idea-frontend-fir/idea-fir-low-level-api/tests/org/jetbrains/kotlin/idea/fir/low/level/api/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java b/idea/idea-frontend-fir/idea-fir-low-level-api/tests/org/jetbrains/kotlin/idea/fir/low/level/api/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java index c2e8f770db1..4291c8590df 100644 --- a/idea/idea-frontend-fir/idea-fir-low-level-api/tests/org/jetbrains/kotlin/idea/fir/low/level/api/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java +++ b/idea/idea-frontend-fir/idea-fir-low-level-api/tests/org/jetbrains/kotlin/idea/fir/low/level/api/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java @@ -23274,6 +23274,11 @@ public class DiagnosisCompilerTestFE10TestdataTestGenerated extends AbstractDiag } @Test + @TestMetadata("propertyInCompanionOfEnum.kt") + public void testPropertyInCompanionOfEnum() throws Exception { + runTest("compiler/testData/diagnostics/tests/resolve/propertyInCompanionOfEnum.kt"); + } + @TestMetadata("resolveAnnotatedLambdaArgument.kt") public void testResolveAnnotatedLambdaArgument() throws Exception { runTest("compiler/testData/diagnostics/tests/resolve/resolveAnnotatedLambdaArgument.kt");