From 877b53201be4d3d9f1b7ddc154c44fa63ef38deb Mon Sep 17 00:00:00 2001 From: Dmitriy Novozhilov Date: Fri, 29 Oct 2021 14:50:41 +0300 Subject: [PATCH] [FIR] Prohibit access to members of companion of enum from enum entry initializer ^KT-49110 --- .../api/fir/diagnostics/KtFirDiagnostics.kt | 2 +- .../fir/diagnostics/KtFirDiagnosticsImpl.kt | 2 +- ...CompilerTestFE10TestdataTestGenerated.java | 12 +++ ...irOldFrontendDiagnosticsTestGenerated.java | 12 +++ ...DiagnosticsWithLightTreeTestGenerated.java | 12 +++ .../diagnostics/FirDiagnosticsList.kt | 2 +- .../fir/analysis/diagnostics/FirErrors.kt | 2 +- .../checkers/CommonDeclarationCheckers.kt | 5 ++ ...umCompanionInEnumConstructorCallChecker.kt | 78 +++++++++++++++++++ .../kotlin/fir/visitors/FirTransformerUtil.kt | 8 ++ .../uninitializedCompanionOfEnum_after.fir.kt | 39 ++++++++++ .../uninitializedCompanionOfEnum_after.kt | 1 - ...uninitializedCompanionOfEnum_before.fir.kt | 12 +-- 13 files changed, 176 insertions(+), 11 deletions(-) create mode 100644 compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirEnumCompanionInEnumConstructorCallChecker.kt create mode 100644 compiler/testData/diagnostics/tests/controlFlowAnalysis/uninitializedCompanionOfEnum_after.fir.kt diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnostics.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnostics.kt index aca3b2ac851..2250ce0a0b2 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnostics.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnostics.kt @@ -2028,7 +2028,7 @@ sealed class KtFirDiagnostic : KtDiagnosticWithPsi { abstract val enumEntry: KtSymbol } - abstract class UninitializedEnumCompanion : KtFirDiagnostic() { + abstract class UninitializedEnumCompanion : KtFirDiagnostic() { override val diagnosticClass get() = UninitializedEnumCompanion::class abstract val enumClass: KtClassLikeSymbol } diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnosticsImpl.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnosticsImpl.kt index 5a0a0aaebe5..d74bbd1b039 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnosticsImpl.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnosticsImpl.kt @@ -2446,7 +2446,7 @@ internal class UninitializedEnumCompanionImpl( override val enumClass: KtClassLikeSymbol, override val firDiagnostic: KtPsiDiagnostic, override val token: ValidityToken, -) : KtFirDiagnostic.UninitializedEnumCompanion(), KtAbstractFirDiagnostic +) : KtFirDiagnostic.UninitializedEnumCompanion(), KtAbstractFirDiagnostic internal class ValReassignmentImpl( override val variable: KtVariableLikeSymbol, 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 790bc7fdbe6..3a0a80000f0 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 @@ -5751,6 +5751,18 @@ public class DiagnosisCompilerTestFE10TestdataTestGenerated extends AbstractDiag runTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/tryWithAssignmentUsedInCatch.kt"); } + @Test + @TestMetadata("uninitializedCompanionOfEnum_after.kt") + public void testUninitializedCompanionOfEnum_after() throws Exception { + runTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/uninitializedCompanionOfEnum_after.kt"); + } + + @Test + @TestMetadata("uninitializedCompanionOfEnum_before.kt") + public void testUninitializedCompanionOfEnum_before() throws Exception { + runTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/uninitializedCompanionOfEnum_before.kt"); + } + @Test @TestMetadata("UninitializedEnumCompanionVals.kt") public void testUninitializedEnumCompanionVals() 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 611cd595157..3ae6431e0a2 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 @@ -5751,6 +5751,18 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti runTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/tryWithAssignmentUsedInCatch.kt"); } + @Test + @TestMetadata("uninitializedCompanionOfEnum_after.kt") + public void testUninitializedCompanionOfEnum_after() throws Exception { + runTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/uninitializedCompanionOfEnum_after.kt"); + } + + @Test + @TestMetadata("uninitializedCompanionOfEnum_before.kt") + public void testUninitializedCompanionOfEnum_before() throws Exception { + runTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/uninitializedCompanionOfEnum_before.kt"); + } + @Test @TestMetadata("UninitializedEnumCompanionVals.kt") public void testUninitializedEnumCompanionVals() 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 3e0a4e96737..a048ebd3835 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 @@ -5751,6 +5751,18 @@ public class FirOldFrontendDiagnosticsWithLightTreeTestGenerated extends Abstrac runTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/tryWithAssignmentUsedInCatch.kt"); } + @Test + @TestMetadata("uninitializedCompanionOfEnum_after.kt") + public void testUninitializedCompanionOfEnum_after() throws Exception { + runTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/uninitializedCompanionOfEnum_after.kt"); + } + + @Test + @TestMetadata("uninitializedCompanionOfEnum_before.kt") + public void testUninitializedCompanionOfEnum_before() throws Exception { + runTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/uninitializedCompanionOfEnum_before.kt"); + } + @Test @TestMetadata("UninitializedEnumCompanionVals.kt") public void testUninitializedEnumCompanionVals() throws Exception { diff --git a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt index 929bb321b31..b1cbaef10f1 100644 --- a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt +++ b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt @@ -1023,7 +1023,7 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") { val UNINITIALIZED_ENUM_ENTRY by error { parameter("enumEntry") } - val UNINITIALIZED_ENUM_COMPANION by error(PositioningStrategy.REFERENCE_BY_QUALIFIED) { + val UNINITIALIZED_ENUM_COMPANION by error(PositioningStrategy.REFERENCE_BY_QUALIFIED) { parameter("enumClass") } val VAL_REASSIGNMENT by error { diff --git a/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt b/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt index 4aaedf3a7da..ec02884b4e1 100644 --- a/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt +++ b/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt @@ -559,7 +559,7 @@ object FirErrors { val UNINITIALIZED_VARIABLE by error1() val UNINITIALIZED_PARAMETER by error1() val UNINITIALIZED_ENUM_ENTRY by error1() - val UNINITIALIZED_ENUM_COMPANION by error1(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED) + val UNINITIALIZED_ENUM_COMPANION by error1(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED) val VAL_REASSIGNMENT by error1>() val VAL_REASSIGNMENT_VIA_BACKING_FIELD by deprecationError1(RestrictionOfValReassignmentViaBackingField) val CAPTURED_VAL_INITIALIZATION by error1() diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/CommonDeclarationCheckers.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/CommonDeclarationCheckers.kt index e5eeabf5d73..8e3c602a4ec 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/CommonDeclarationCheckers.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/CommonDeclarationCheckers.kt @@ -156,4 +156,9 @@ object CommonDeclarationCheckers : DeclarationCheckers() { get() = setOf( FirAnonymousInitializerInInterfaceChecker ) + + override val enumEntryCheckers: Set + get() = setOf( + FirEnumCompanionInEnumConstructorCallChecker, + ) } diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirEnumCompanionInEnumConstructorCallChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirEnumCompanionInEnumConstructorCallChecker.kt new file mode 100644 index 00000000000..94bdd567e6f --- /dev/null +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirEnumCompanionInEnumConstructorCallChecker.kt @@ -0,0 +1,78 @@ +/* + * 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.fir.analysis.checkers.declaration + +import org.jetbrains.kotlin.descriptors.ClassKind +import org.jetbrains.kotlin.diagnostics.DiagnosticReporter +import org.jetbrains.kotlin.diagnostics.reportOn +import org.jetbrains.kotlin.fir.FirElement +import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext +import org.jetbrains.kotlin.fir.analysis.checkers.toRegularClassSymbol +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors +import org.jetbrains.kotlin.fir.declarations.FirEnumEntry +import org.jetbrains.kotlin.fir.declarations.FirRegularClass +import org.jetbrains.kotlin.fir.declarations.primaryConstructorIfAny +import org.jetbrains.kotlin.fir.expressions.* +import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol +import org.jetbrains.kotlin.fir.visitors.FirVisitorVoid +import org.jetbrains.kotlin.fir.visitors.acceptAllElements +import org.jetbrains.kotlin.utils.addToStdlib.lastIsInstanceOrNull + +object FirEnumCompanionInEnumConstructorCallChecker : FirEnumEntryChecker() { + override fun check(declaration: FirEnumEntry, context: CheckerContext, reporter: DiagnosticReporter) { + val enumClass = context.containingDeclarations.lastIsInstanceOrNull() ?: return + if (enumClass.classKind != ClassKind.ENUM_CLASS) return + val companionOfEnumSymbol = enumClass.companionObject?.symbol ?: return + val initializerObject = (declaration.initializer as? FirAnonymousObjectExpression)?.anonymousObject ?: return + val delegatingConstructorCall = initializerObject.primaryConstructorIfAny(context.session)?.resolvedDelegatedConstructorCall ?: return + val visitor = Visitor(context, reporter, companionOfEnumSymbol) + delegatingConstructorCall.argumentList.acceptChildren(visitor) + } + + private class Visitor( + val context: CheckerContext, + val reporter: DiagnosticReporter, + val companionSymbol: FirRegularClassSymbol + ) : FirVisitorVoid() { + override fun visitElement(element: FirElement) { + element.acceptChildren(this) + } + + override fun visitFunctionCall(functionCall: FirFunctionCall) { + val needVisitReceiver = checkQualifiedAccess(functionCall) + functionCall.argumentList.acceptChildren(this) + if (needVisitReceiver) { + functionCall.explicitReceiver?.accept(this) + } + } + + override fun visitPropertyAccessExpression(propertyAccessExpression: FirPropertyAccessExpression) { + val needVisitReceiver = checkQualifiedAccess(propertyAccessExpression) + if (needVisitReceiver) { + propertyAccessExpression.explicitReceiver?.accept(this) + } + } + + + private fun checkQualifiedAccess(expression: FirQualifiedAccessExpression): Boolean { + val extensionReceiver = expression.extensionReceiver + if (extensionReceiver !is FirResolvedQualifier && extensionReceiver !is FirThisReceiverExpression) return true + + val receiverSymbol = expression.toResolvedCallableSymbol() + ?.resolvedReceiverTypeRef + ?.toRegularClassSymbol(context.session) + ?: return true + + if (receiverSymbol == companionSymbol) { + val source = expression.extensionReceiver.source ?: expression.source + reporter.reportOn(source, FirErrors.UNINITIALIZED_ENUM_COMPANION, companionSymbol, context) + return false + } + + return true + } + } +} diff --git a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/visitors/FirTransformerUtil.kt b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/visitors/FirTransformerUtil.kt index 8be46c99a60..cb5f3b98d17 100644 --- a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/visitors/FirTransformerUtil.kt +++ b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/visitors/FirTransformerUtil.kt @@ -39,3 +39,11 @@ inline fun MutableList.transformInplace(transformer: FirT iterator.set(result) } } + +fun List.acceptAllElements(visitor: FirVisitor, data: D) { + forEach { it.accept(visitor, data) } +} + +fun List.acceptAllElements(visitor: FirVisitorVoid) { + forEach { it.accept(visitor) } +} diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/uninitializedCompanionOfEnum_after.fir.kt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/uninitializedCompanionOfEnum_after.fir.kt new file mode 100644 index 00000000000..1940da856a3 --- /dev/null +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/uninitializedCompanionOfEnum_after.fir.kt @@ -0,0 +1,39 @@ +// LANGUAGE: +ProhibitAccessToEnumCompanionMembersInEnumConstructorCall +// ISSUE: KT-49110 + +enum class SomeEnum(val x: Int) { + A(companionFun().length),// UNINITIALIZED_ENUM_COMPANION + B(companionProp.length), // UNINITIALIZED_VARIABLE + + C(SomeEnum.companionFun().length), + D(SomeEnum.companionProp.length), + + E(SomeEnum.Companion.companionFun().length), + F(SomeEnum.Companion.companionProp.length); // UNINITIALIZED_VARIABLE + + companion object { + val companionProp = "someString" + fun companionFun(): String = "someString" + } +} + +enum class OtherEnum(val x: Int) { + G(extensionFun().length), // UNINITIALIZED_ENUM_COMPANION + H(extensionProp.length), + + I(OtherEnum.extensionFun().length), + J(OtherEnum.extensionProp.length), + + K(OtherEnum.Companion.extensionFun().length), + L(OtherEnum.Companion.extensionProp.length); + + companion object { + val companionProp = "someString" + fun companionFun(): String = "someString" + } +} + +fun OtherEnum.Companion.extensionFun(): String = companionFun() +val OtherEnum.Companion.extensionProp: String + get() = companionProp + diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/uninitializedCompanionOfEnum_after.kt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/uninitializedCompanionOfEnum_after.kt index cf805150c1a..b1bd487f067 100644 --- a/compiler/testData/diagnostics/tests/controlFlowAnalysis/uninitializedCompanionOfEnum_after.kt +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/uninitializedCompanionOfEnum_after.kt @@ -1,5 +1,4 @@ // LANGUAGE: +ProhibitAccessToEnumCompanionMembersInEnumConstructorCall -// FIR_IDENTICAL // ISSUE: KT-49110 enum class SomeEnum(val x: Int) { diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/uninitializedCompanionOfEnum_before.fir.kt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/uninitializedCompanionOfEnum_before.fir.kt index 819b4879c24..55b53be0298 100644 --- a/compiler/testData/diagnostics/tests/controlFlowAnalysis/uninitializedCompanionOfEnum_before.fir.kt +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/uninitializedCompanionOfEnum_before.fir.kt @@ -18,14 +18,14 @@ enum class SomeEnum(val x: Int) { } enum class OtherEnum(val x: Int) { - G(extensionFun().length), // UNINITIALIZED_ENUM_COMPANION - H(extensionProp.length), + G(extensionFun().length), // UNINITIALIZED_ENUM_COMPANION + H(extensionProp.length), - I(OtherEnum.extensionFun().length), - J(OtherEnum.extensionProp.length), + I(OtherEnum.extensionFun().length), + J(OtherEnum.extensionProp.length), - K(OtherEnum.Companion.extensionFun().length), - L(OtherEnum.Companion.extensionProp.length); + K(OtherEnum.Companion.extensionFun().length), + L(OtherEnum.Companion.extensionProp.length); companion object { val companionProp = "someString"